LinkedList的ListIterator.next().toString()抛出ConcurrentModificationException问题
解决卡牌游戏中的
ConcurrentModificationException问题 我来帮你分析并解决这个问题,你遇到的异常本质是迭代器的fail-fast机制在起作用,咱们一步步拆解:
问题根源分析
你代码里的核心矛盾在于:
// 先创建迭代器,绑定当前的cards集合状态 ListIterator<Card> listIterator = this.cards.listIterator(); // 紧接着修改了cards集合(重新生成牌组+洗牌) createCardList(); shuffleCardList();
迭代器是基于创建时的集合状态工作的,当你后续修改了集合的结构(比如重新初始化、洗牌改变元素顺序),迭代器会检测到集合状态和它预期的不一致,直接抛出ConcurrentModificationException,防止出现不可预期的遍历结果。
另外你的逻辑还有个小问题:每次循环都重新生成牌组,用户的遍历操作(比如翻下一张牌)根本没法保留进度,每次都是新的牌组,这不符合卡牌游戏的正常逻辑。
解决方案步骤
1. 调整代码执行顺序,初始化牌组只做一次
把牌组初始化和洗牌的逻辑移到循环外面,确保迭代器是在集合稳定后创建的:
public void runGame() { Scanner scanner = new Scanner(System.in); boolean quit = false; boolean forward = true; // 先初始化并洗牌牌组,只执行一次 createCardList(); shuffleCardList(); printMenu(); // 初始化迭代器,复用这个引用保留遍历进度 ListIterator<Card> listIterator = this.cards.listIterator(); while (!quit) { int action = scanner.nextInt(); scanner.nextLine(); switch (action) { case 0: System.out.println("Thanks for playing"); quit = true; break; case 1: if (!forward) { // 从向后遍历切换到向前时,调整迭代器位置 if (listIterator.hasNext()) { listIterator.next(); } forward = true; } if (listIterator.hasNext()) { // 重写了toString(),直接打印对象即可,无需显式调用 System.out.println("Next card is " + listIterator.next()); } else { System.out.println("We have reached the end of the card deck"); forward = false; } break; // 可选:添加向后遍历的逻辑 case 2: if (forward) { if (listIterator.hasPrevious()) { listIterator.previous(); } forward = false; } if (listIterator.hasPrevious()) { System.out.println("Previous card is " + listIterator.previous()); } else { System.out.println("We have reached the start of the card deck"); forward = true; } break; // 可选:添加重新洗牌的逻辑,洗牌后要重新创建迭代器 case 3: shuffleCardList(); listIterator = this.cards.listIterator(); forward = true; System.out.println("Deck shuffled, starting from the first card"); break; } } scanner.close(); // 记得关闭Scanner释放资源 }
2. 关键优化点说明
- 复用迭代器引用:把迭代器创建移到循环外面,这样用户的遍历进度会被保留,不会每次操作都回到牌组开头。
- 洗牌后重新创建迭代器:如果需要支持重新洗牌,洗牌后必须重新生成迭代器,因为洗牌会修改集合结构,原迭代器会失效。
- 简化toString调用:你已经在Card类中重写了
toString(),直接打印Card对象时Java会自动调用该方法,无需显式写toString()。
额外检查点
确认你的shuffleCardList()实现,如果是用Collections.shuffle(this.cards),那它会直接修改LinkedList的元素顺序,属于触发fail-fast的结构性修改,这时候必须重新创建迭代器,和上面的case3逻辑一致。
内容的提问来源于stack exchange,提问作者Oscar Powell
相关产品推荐
相关产品推荐

