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

自定义XList遍历移除元素触发ConcurrentModificationException问题排查(不可修改Main代码)

排查并解决XList中forEachWithIndex的ConcurrentModificationException问题

异常原因拆解

你遇到的ConcurrentModificationException是Java集合快速失败(fail-fast)机制的典型表现,具体原因如下:

  • 你的XList.forEachWithIndex方法使用了ArrayList内置的Iterator进行遍历。ArrayList会维护一个modCount变量,记录集合结构修改(添加、删除元素)的次数;而迭代器会保存一个expectedModCount副本,每次调用next()时都会校验两者是否一致。
  • 在Main类的第二、三次调用中,你在Lambda里直接调用了lmod.remove(e)或lmod.remove(i)——这些操作会修改集合的modCount,但迭代器的expectedModCount并没有同步更新,导致下一次调用iterator.next()时触发校验,直接抛出异常。

顺带提一句:第一次调用set(i, e*2)没报错,是因为ArrayList的set方法只是修改元素值,不会改变集合结构,所以不会修改modCount,自然不会触发异常。

不修改Main类的解决方案

我们只需要修改XList的forEachWithIndex方法,避开迭代器的快速失败检查即可。最直接的方式是用基于索引的普通for循环替代迭代器遍历:

修改后的forEachWithIndex代码:

public void forEachWithIndex(BiConsumer<? super T, ? super Integer> consumer) {
    // 先获取当前集合大小,避免遍历过程中size变化引发的遍历异常
    int currentSize = this.size();
    for (int counter = 0; counter < currentSize; counter++) {
        // 直接通过索引获取元素,绕开迭代器的修改校验
        consumer.accept(this.get(counter), counter);
    }
}

额外说明

用索引循环后,虽然不会再抛出异常,但要注意Main类第三次调用的逻辑:if (i % 2 == 0) lmod.remove(i)。删除元素后集合元素会前移,后续索引对应的元素会发生变化,这可能导致最终结果和你预期的有差异——但由于我们不能修改Main类代码,只能保证不抛出异常,逻辑层面的问题需要由原代码负责。

内容的提问来源于stack exchange,提问作者mattonaly

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:37:35