增强for循环中调用list.remove()会抛出ConcurrentModificationException,根本原因是fail-fast机制:迭代器创建时快照modCount为expectedModCount,list.remove()使modCount自增但expectedModCount未更新,下次next()校验失败即报错。

增强 for 循环中修改集合会触发 ConcurrentModificationException,根本原因不是“并发”,而是 Java 集合的快速失败(fail-fast)机制主动拦截了非法的结构性修改。
为什么增强 for 循环里调用 list.remove() 会报错
增强 for 循环是语法糖,编译后等价于显式使用 Iterator:
- 循环开始时,
list.iterator()创建迭代器,并将当前集合的modCount(结构性修改计数器)快照为expectedModCount; - 每次调用
iterator.next()前,都会检查modCount == expectedModCount; - 你在循环体内写
list.remove(s),会令modCount++,但迭代器的expectedModCount并未更新; - 下一次
next()执行时校验失败,立即抛出异常。
哪些操作属于“结构性修改”
只要改变集合大小或内部结构,都算结构性修改,包括:
-
add()、remove()、clear() -
removeAll()、retainAll() -
ArrayList的扩容(如ensureCapacity()触发) -
HashMap的put()引发 rehash
注意:list.set(i, x) 或修改元素对象内部字段,不改变集合结构,不会触发异常。
立即学习“Java免费学习笔记(深入)”;
安全修改集合的几种可靠方式
核心原则:不绕过迭代器直接操作集合本身。
-
用 Iterator.remove():唯一被设计用于遍历时删除的方式,它会同步更新
expectedModCount; -
收集后批量删除:先用
ArrayList存下要删的元素,循环结束后调用list.removeAll(toRemove); -
倒序索引遍历:适用于
List,从size-1往 0 遍历,删除时不干扰后续下标; -
Java 8 的 removeIf():简洁安全,内部由集合自身控制迭代与删除逻辑,例如
list.removeIf(s -> s.isEmpty());
常见误区澄清
普通 for (int i = 0; i 中调用 <code>list.remove(i) 虽不抛 CME,但容易跳过元素(因删除后后续元素前移,而 i 仍自增);若调用 list.remove(obj)(按对象值删),底层仍是遍历查找,效率低且仍可能漏删。这不是“安全”,只是没触发 fail-fast 检查。


















