You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:52:56