linkedlist迭代器不避免并发异常,而是通过fail-fast机制暴露问题:modcount在结构性修改时递增,迭代器创建时记录expectedmodcount,每次操作前校验二者是否一致,不一致则立即抛出concurrentmodificationexception。

LinkedList 的双向链表迭代器本身不“避免”并发修改异常,而是通过快速失败(fail-fast)机制暴露问题——它不掩盖错误,而是第一时间抛出 ConcurrentModificationException,迫使开发者主动处理线程安全或遍历逻辑问题。
迭代器的 modCount 检查机制
LinkedList 继承自 AbstractList,内部维护一个 modCount(修改计数器),每次调用 add、remove、clear 等结构性修改方法时都会递增。而迭代器(包括 Iterator 和 ListIterator)在创建时会记录当时的 modCount 值为 expectedModCount;每次调用 next()、hasNext()、remove() 或 set() 前,都会校验两者是否一致:
- 不一致 → 立即抛出
ConcurrentModificationException - 一致 → 正常执行
这个检查发生在迭代器内部,与是否“双向”无关,但 ListIterator 因支持双向遍历和中间修改,更常被用于需要边遍历边调整的场景。
真正安全的遍历+修改方式
要让操作不触发异常,关键不是绕过检查,而是让修改动作由迭代器自身发起,从而同步更新 expectedModCount:
-
Iterator.remove():删除刚返回的元素,内部会调用LinkedList.this.remove()并同步更新modCount和expectedModCount -
ListIterator.remove()/ListIterator.set()/ListIterator.add():同理,这些方法都经过迭代器封装,能保持状态一致性
例如删除所有 "a":
Iterator<string> it = list.iterator();
while (it.hasNext()) {
if ("a".equals(it.next())) {
it.remove(); // ✅ 安全,不会抛异常
}
}</string>
为什么 for 循环 + remove(i) 仍可能出错
即使不用迭代器,直接用索引遍历也需谨慎:
- 正向循环
for (int i = 0; i 中调用 <code>list.remove(i)会导致后续元素前移,i自增后跳过下一个元素 - 反向循环
for (int i = list.size()-1; i >= 0; i--)可避免跳过,但仍是“集合自身修改”,未走迭代器路径,modCount仍会变 —— 如果此时有另一个迭代器正在使用,它仍会失败
也就是说:单线程下反向 for 是可行的,但多线程或混合使用迭代器时,依然危险。
多线程环境下的正确做法
LinkedList 本身不是线程安全的。若需并发访问,不能依赖迭代器机制来“解决”问题,而应换用线程安全方案:
- 用
Collections.synchronizedList(new LinkedList())包装,但需手动同步迭代过程(如对整个遍历块加锁) - 改用
ConcurrentLinkedQueue(注意:它不实现List,无索引访问,但支持高并发插入/删除) - 若必须保留 List 语义且并发读多写少,可考虑
CopyOnWriteArrayList(适合迭代远多于修改的场景)
单纯靠“写个更聪明的迭代器”无法消除并发修改异常——它是设计用来报警的,不是用来容忍竞态的。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











