Fail-Fast是Java集合的快速失败机制,通过modCount与expectedModCount比对,在next()或remove()时检测到外部结构性修改(如add/remove)即抛ConcurrentModificationException;它非并发安全保证,仅单线程下暴露错误,安全修改须用it.remove()或并发容器。

Java 中的 Iterator 迭代器失效机制(Fail-Fast) 是在遍历集合过程中,检测到集合结构被意外修改时,主动抛出 ConcurrentModificationException 的保护机制。它不是“真正实时检测并发修改”,而是一种快速失败的校验策略,核心靠的是 modCount 和 expectedModCount 两个计数器的比对。
Fail-Fast 触发的关键条件
Fail-Fast 不是每次修改都触发,只在特定时机检查并抛异常:
-
调用迭代器的 next() 或 remove() 方法时(即实际取元素或删当前元素时),才会校验
expectedModCount == modCount -
集合自身结构被修改:比如调用
ArrayList.add()、remove()、clear()等改变size或底层数组/节点结构的方法 - 修改操作不是由当前 Iterator 发起的:例如用 for-each 循环(本质是用 Iterator)的同时,又直接调用 list.remove(),就会导致不一致
典型触发场景示例
以下代码会立即在第二次 it.next() 时抛出 ConcurrentModificationException:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> it = list.iterator();
System.out.println(it.next()); // "a"
list.remove("b"); // ⚠️ 集合结构被外部修改 → modCount++
it.next(); // 检查时发现 expectedModCount != modCount → 抛异常
注意:如果只是修改元素内容(如 list.set(1, "x")),不改变结构,modCount 不变,就不会触发 Fail-Fast。
立即学习“Java免费学习笔记(深入)”;
为什么 HashMap 的迭代器也 Fail-Fast?
和 ArrayList 类似,HashMap 内部也有 modCount 字段,记录哈希表结构变化次数(如扩容、链表转红黑树、节点增删)。Iterator 在 nextNode() 前同样校验该值。所以遍历时直接调用 map.put() 或 remove(),也会触发异常。
如何安全地边遍历边修改?
不能依赖“不报错就安全”,必须用正确方式:
-
使用 Iterator 自带的 remove() 方法:它会同步更新
expectedModCount,保持一致 -
收集待删元素,遍历结束后统一删除:如用临时 Set/List 记录,再调用
removeAll() -
改用线程安全且支持并发修改的集合:如
CopyOnWriteArrayList(其 Iterator 基于快照,不检查 modCount)、ConcurrentHashMap(弱一致性迭代器,不抛 CME)
Fail-Fast 是一种调试友好型设计,目的不是解决并发问题,而是尽早暴露“非协同的结构修改”,避免产生隐蔽的数据错乱。理解它的触发边界,才能写出既安全又符合预期的遍历逻辑。


















