for-each中调用list.remove()会抛ConcurrentModificationException,因迭代器的modCount与expectedModCount不一致;安全做法是使用iterator.remove()或倒序遍历等。

在 for-each 循环中直接调用 list.remove() 会抛出 ConcurrentModificationException,这不是线程并发导致的“真并发”,而是 Java 集合的快速失败(fail-fast)机制主动检测到结构被非法修改而抛出的异常。本质是迭代器内部维护的 modCount 与期望值 expectedModCount 不一致。
for-each 底层就是 Iterator,且不支持边遍历边 remove
Java 中的 for-each 循环(如 for (String s : list))在编译后会被转为基于 Iterator 的标准循环:
- 调用
list.iterator()获取迭代器 - 每次循环前调用
it.hasNext() - 每次取元素调用
it.next()
而 ArrayList.Iterator(以 ArrayList 为例)是一个私有内部类,它在构造时会把当前 ArrayList.modCount 赋值给自己的 expectedModCount。之后每次调用 next() 或 remove() 前,都会检查 modCount == expectedModCount;一旦不等,立刻抛出 ConcurrentModificationException。
当你在 for-each 中写 list.remove(x),实际是调用了 ArrayList.remove() —— 它会修改 modCount++,但迭代器里的 expectedModCount 没变,下一次 next() 就触发校验失败。
为什么 iterator.remove() 却安全?
Iterator.remove() 是唯一被设计为“遍历时安全删除”的方式,原因在于:
- 它内部会同步更新
expectedModCount = modCount - 它还维护了
lastRet状态,确保只删上一次next()返回的元素 - 它本身是迭代器协议的一部分,和 fail-fast 机制协同工作
所以正确写法是:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("abc".equals(s)) {
it.remove(); // ✅ 安全
}
}
其他看似“绕过”的写法,哪些真有效?
常见尝试及原理分析:
- 倒序 for 循环(i = size-1 → 0):可行。因为删除索引 i 处元素不影响 i 之前的索引,不会跳过元素,也未触发 Iterator,自然不触发 fail-fast
- 正序 for 循环但手动控制 i--:可行。删除后 i 减一,抵消后续 i++,避免漏判下一个位置
- 先收集待删元素,再 removeAll():安全。两次遍历,无结构修改与迭代交叉
- 用 CopyOnWriteArrayList:可运行但不推荐用于普通场景。它的迭代器基于快照,不检查 modCount,但写操作开销大、内存占用高,且无法保证实时一致性
如何从源码确认?关键断点位置
以 OpenJDK 17 的 ArrayList$Itr.next() 为例,核心逻辑如下:
public E next() {
checkForComodification(); // ← 关键校验入口
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount) // ← 这里不等就炸
throw new ConcurrentModificationException();
}
调试时,在 checkForComodification 打断点,观察 modCount 和 expectedModCount 的值变化,就能清晰看到:你调一次 list.remove(),modCount 加 1,而 expectedModCount 停滞,下次 next 必然失败。

















