ConcurrentModificationException本质是迭代器通过每次调用next()等方法时显式比较modCount与expectedModCount是否相等来实现的快速失败机制;前者为集合结构修改计数器,后者为迭代器创建时保存的期望值,不一致即抛异常。

Java 集合的迭代器失效(ConcurrentModificationException)本质是通过 modCount 和 expectedModCount 的校验实现的。这个机制不是“自动检查”,而是每次调用 next()、remove() 等迭代器方法时,**显式比较二者是否相等**;不一致就立即抛出异常。
modCount 与 expectedModCount 是什么
modCount 是集合内部维护的修改计数器(如 ArrayList、HashMap 中的 transient int modCount),每次结构修改(增、删、清空等)都会自增。
expectedModCount 是迭代器创建时从集合拷贝的 modCount 值,代表“我期望集合保持这个修改状态”。迭代过程中,只要集合被外部线程或同一线程的其他代码修改,modCount 就会变,而迭代器仍拿着旧的 expectedModCount,下次调用 next() 就会发现不匹配。
如何触发校验(即“检查”时机)
校验发生在迭代器核心方法的开头,不是后台监听,也不是定时检测:
-
Iterator.next():先调用checkForComodification() -
Iterator.remove():同样先校验,再执行删除逻辑 -
ListIterator.previous()、set()、add()等也均会校验
以 ArrayList$Itr.next() 源码片段为例:
public E next() {
checkForComodification(); // ← 就在这里检查
...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
怎么确认是它导致的异常
遇到 ConcurrentModificationException 时,按以下顺序排查:
立即学习“Java免费学习笔记(深入)”;
- 看堆栈最上层是否为
next()、remove()或forEachRemaining()等迭代器方法 - 检查迭代过程中是否在循环体内直接调用了集合的
add()、remove()、clear()(非迭代器自身的remove()) - 多线程场景下,确认是否有其他线程正在修改该集合(即使只读线程也在遍历,写线程一改就崩)
- 注意隐藏修改:比如用
stream().filter(...).collect(...)同时又在遍历原集合,看似没改,但若 filter 内部有副作用(如调用外部 remove),也会触发
如何避免或绕过这个检查(安全做法)
不推荐“绕过”校验(如反射修改 expectedModCount),应改用正确方式:
- 单线程:用迭代器自己的
remove()方法删除(它会同步更新expectedModCount) - 需要边遍历边增删多个元素 → 先收集待操作元素(如
List<E> toRemove = new ArrayList<>()),遍历完再批量操作 - 多线程环境 → 改用线程安全集合:
CopyOnWriteArrayList(读不加锁、写复制)、ConcurrentHashMap(分段/ CAS 控制) - 临时需要“允许并发修改” → 使用
Iterator之外的方式,如普通 for 循环(基于索引)或增强 for 的底层等价代码(但注意:增强 for 仍是用迭代器,不能规避)


















