Java迭代器通过每次调用next()、hasNext()或remove()前比对expectedModCount与modCount实现fail-fast机制;modCount为volatile确保可见性,expectedModCount仅初始化一次且仅迭代器自身remove()会更新,不等则立即抛ConcurrentModificationException终止遍历。

Java 迭代器内部通过每次调用 next()、hasNext() 或 remove() 前主动比对 expectedModCount 和集合的 modCount 来判断结构是否被意外修改。这个比对不是一次性或延迟发生的,而是严格嵌入在迭代流程的关键入口处。
比对发生在每一次迭代操作开始前
以 ArrayList 的 Itr 内部类为例,next() 方法第一件事就是调用 checkForComodification():
- 该方法只做一件事:
if (modCount != expectedModCount) throw new ConcurrentModificationException(); - 也就是说,只要下一次要取元素,就必须先过这道“版本校验关”
- 哪怕只调用一次
hasNext(),它同样会触发校验(因为需要确认下一个节点是否存在,依赖当前结构一致性)
modCount 是 volatile 的,确保可见性
modCount 在 AbstractList、ArrayList、HashMap 等类中均被声明为 volatile:
- 保证多线程环境下,一个线程对
modCount的自增(如add()后modCount++)能立即被其他线程的迭代器读到 - 避免因 CPU 缓存不一致导致本该失败的比对“侥幸通过”
- 但要注意:volatile 不保证原子性,只是保障读写可见——而这里只需要“看到最新值”,不需要复合操作原子性
expectedModCount 只初始化一次,不再自动更新
迭代器构造时完成绑定,之后完全靠开发者或迭代器自身逻辑维持同步:
立即学习“Java免费学习笔记(深入)”;
- 创建
iterator()时,expectedModCount = modCount—— 这是一次快照,不是引用 - 后续集合被外部调用
remove()、add()、clear(),modCount增加,但expectedModCount保持不变 - 只有迭代器自己的
remove()方法会在删除后执行expectedModCount = modCount,实现“自我认账”
比对失败即终止,不尝试恢复或跳过
Fail-fast 的核心是“立即响应”,不是容错:
- 一旦发现不等,立刻抛出
ConcurrentModificationException,遍历彻底中断 - 不会尝试重试、重置、或继续走完剩余逻辑
- 异常堆栈会明确指向
next()或hasNext()调用点,方便定位哪一行代码破坏了迭代契约


















