ArrayList的ListIterator在双向遍历时同样具有fail-fast机制:当外部结构性修改导致modCount与expectedModCount不一致时,下次调用next()、previous()、add()、set()或remove()即抛ConcurrentModificationException;其自身add()/remove()会同步更新expectedModCount,但set()虽不改结构仍受光标位置约束;多线程下仅靠ListIterator无法保证线程安全,需用CopyOnWriteArrayList或外部同步。

ArrayList 的 listIterator() 在双向遍历时,其 fail-fast 行为与普通迭代器一致:只要在迭代过程中,有其他线程或同一线程通过非迭代器方法(如 add()、remove()、set() 等)修改了列表结构,下一次调用 next()、previous()、add()、set() 或 remove() 时就会抛出 ConcurrentModificationException。
fail-fast 的触发时机取决于“结构性修改”和“预期修改次数”
ArrayList 内部维护一个 modCount(修改计数器),每次调用 add()、remove()、clear() 等改变大小的操作都会递增它。而 ListIterator 实例在创建时会记录当时的 modCount 值到自己的 expectedModCount 中。后续每次调用它的操作前,都会检查两者是否一致:
- 不一致 → 立即抛出
ConcurrentModificationException - 一致 → 继续执行,并在执行
add()或remove()后同步更新expectedModCount(因为这些是迭代器自身允许的结构性修改)
迭代器自身的 add() 和 remove() 不会触发 fail-fast
ListIterator 提供的 add() 和 remove() 是安全的,它们会同步更新 expectedModCount,从而避免自我干扰。例如:
ListIterator<String> iter = list.listIterator();
iter.next(); // 移动到第一个元素后
iter.add("new"); // ✅ 合法:插入后 expectedModCount 自动+1
iter.previous(); // ✅ 仍可继续遍历
但若在调用 iter.add("new") 后,又用 list.remove(0) 修改原列表,则下次调用 iter.next() 就会失败。
立即学习“Java免费学习笔记(深入)”;
set() 是非结构性修改,但也有检查约束
set(E) 不改变列表大小,不算结构性修改,因此不会增加 modCount;但它要求当前光标位置必须已存在元素(即刚调用过 next() 或 previous()),否则抛 IllegalStateException。它不触发 fail-fast,但若在 set() 前发生了外部结构性修改,仍会在 set() 调用时检查 modCount 并失败。
多线程环境下无法靠 listIterator 规避并发问题
即使只用 ListIterator 的 add()/remove(),也不能保证线程安全。因为:
-
modCount是非 volatile 的,不同线程可能看到过期值 - fail-fast 只是检测机制,不是同步手段
- 两个线程各自持有独立的
ListIterator,彼此无法感知对方的修改
真正需要线程安全时,应改用 CopyOnWriteArrayList 或加外部锁,而非依赖 fail-fast。


















