递归实现随机抽唯一卡牌时出现java.lang.StackOverflowError求助
问题分析与解决方案
原代码的核心问题
Deck初始化错误:
初始化deck时,你把同一个Integer[] deckSet对象重复添加了13次,导致deck里所有元素都是同一个数组的引用。一旦修改其中任意位置的数值,所有deck元素都会同步变化,很快所有卡牌标记都会被置为0,后续调用cardGenerator时永远抽不到有效卡牌,触发无限递归,最终导致StackOverflowError。递归逻辑的缺陷:
即使修复了初始化问题,递归方式也不稳定——当剩余卡牌数量较少时,重复抽到已抽取卡牌的概率会越来越高,递归深度急剧增加,依然可能触发栈溢出。
修复方案一:修正原逻辑(非递归实现)
先修复deck的初始化问题,同时把递归改成循环,避免栈溢出:
import java.util.ArrayList; import java.util.Random; public class App { static ArrayList<Integer[]> deck = new ArrayList<>(); static ArrayList<Integer[]> dealer = new ArrayList<>(); static Random random = new Random(); // 复用Random实例,避免重复创建 static Integer[] cardGenerator() { while (true) { Integer num = random.nextInt(13); Integer shape = random.nextInt(4); Integer[] deckSet = deck.get(num); if (deckSet[shape] == 1) { deckSet[shape] = 0; // 数组是引用类型,修改后直接生效,无需调用deck.set return new Integer[]{num, shape}; } // 抽不到有效牌就继续循环,直到抽到为止 } } public static void main(String[] args) { // 每次循环新建数组,避免引用复用 for (int i = 0; i < 13; i++) { deck.add(new Integer[]{1, 1, 1, 1}); } for (int i = 0; i < 5; i++) { dealer.add(cardGenerator()); } // 打印验证结果 for (Integer[] card : dealer) { System.out.println("卡牌数字:" + card[0] + ",花色:" + card[1]); } } }
更优方案二:洗牌法(推荐)
直接生成完整的52张卡牌列表,打乱顺序后取前5张,逻辑简洁高效,完全避免重复抽取问题:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class App { static List<Integer[]> dealer = new ArrayList<>(); public static void main(String[] args) { // 生成全部52张卡牌 List<Integer[]> fullDeck = new ArrayList<>(); for (int num = 0; num < 13; num++) { for (int shape = 0; shape < 4; shape++) { fullDeck.add(new Integer[]{num, shape}); } } // 洗牌打乱顺序 Collections.shuffle(fullDeck); // 取前5张给dealer dealer.addAll(fullDeck.subList(0, 5)); // 打印验证结果 for (Integer[] card : dealer) { System.out.println("卡牌数字:" + card[0] + ",花色:" + card[1]); } } }
这个方案无需维护已抽取卡牌的标记,通过洗牌直接保证随机性和唯一性,代码更简洁,性能也更稳定。
内容的提问来源于stack exchange,提问作者Lawrance
相关产品推荐
相关产品推荐

