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

为什么LinkedList的remove方法不会触发ConcurrentModificationException?

测试代码
Deque<Employee> employeeDeque = new LinkedList<>();
employeeDeque.offerLast(new Employee("Michael", 250));
employeeDeque.offerLast(new Employee("John", 250));
Iterator iterator = employeeDeque.iterator();
while (iterator.hasNext()) {
    iterator.next();
    employeeDeque.remove(new Employee("Michael", 250));
}

注:以下结论默认Employee类已经重写equals方法,保证remove可以匹配到对应元素

底层原因拆解

ConcurrentModificationException(简称CME)的核心触发逻辑是所有Fail-Fast集合的统一规则:

  • 集合内部维护modCount变量,每次发生结构变更(新增、删除元素)时modCount自增1
  • 迭代器初始化时会记录当前modCount为expectedModCount
  • 只有迭代器主动调用next()、remove()等自身方法时,才会校验expectedModCount和集合当前modCount是否相等,不等就抛出CME,仅修改modCount不会主动触发异常

1. 为什么ArrayList场景下通常会触发CME

ArrayList的remove操作只要成功执行,一定会让modCount自增。大部分遍历场景下,删除元素后迭代器的nextIndex仍然小于新的size,hasNext()会返回true,下次调用next()时就会触发modCount校验抛出异常。
只有极端特例(比如刚好删除元素后nextIndex等于新的size,hasNext()返回false直接结束循环)下ArrayList也不会抛出异常,只是你测试时的参数没有命中这个特例而已。


2. 为什么这段LinkedList代码没有抛出CME

这是完全的场景特例,不是LinkedList的通用特性,刚好绕开了迭代器的校验逻辑:

  • 初始集合只有2个元素,第一次调用iterator.next()后,迭代器的下一个遍历下标nextIndex变为1
  • 调用集合自身的remove删除第一个元素成功后,modCount确实自增了,但集合的size也从2变为1
  • 下次循环执行iterator.hasNext()时,判断逻辑为nextIndex < size即1 < 1,结果为false,循环直接终止,迭代器根本没有机会执行next()触发校验,自然不会抛出CME。
    你可以做对照验证:给集合新增1个元素变成3个,再执行相同逻辑,第二次循环就会正常抛出CME。

3. 为什么LinkedList调用add/offer类方法一定会触发CME

add/offer操作会让集合size变大,还是拿你这段代码的初始场景举例:

  • 2个元素第一次next()后nextIndex为1,调用add方法新增元素后size变为3
  • 下次循环hasNext()判断1 < 3为true,会继续调用next()方法
  • 此时迭代器校验发现expectedModCount和add操作修改后的modCount不一致,直接抛出CME。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 19:36:00