为什么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
相关产品推荐
相关产品推荐

