遍历时直接调用集合的remove()会抛concurrentmodificationexception,因迭代器是fail-fast机制,modcount校验失败;唯一安全方式是iterator.remove()或使用removeif()。

遍历时用 remove() 直接删元素,为什么抛 ConcurrentModificationException
因为大多数 Java 集合(如 ArrayList、HashMap)的迭代器是「快速失败」(fail-fast)的。内部靠一个 modCount 计数器检测结构变更:只要集合被非迭代器方式修改(比如在 for-each 循环里调 list.remove()),下一次调 iterator.next() 就会立刻抛异常。
这不是线程安全问题,单线程下也必现——只要「遍历」和「修改」不是通过同一个迭代器接口发生,就触发检查。
- 错误写法:
for (String s : list) { if (s.isEmpty()) list.remove(s); } - 现象:运行到第二个元素时大概率报
ConcurrentModificationException - 本质:
list.remove()改了modCount,但 for-each 底层的Iterator还在用旧值校验
Iterator.remove() 是唯一安全的遍历中删除方式
迭代器自己提供的 remove() 方法,会在删除后同步更新自己的预期 modCount,绕过校验冲突。这是 JDK 明确保证的线程不安全但单线程安全的用法。
- 必须配对使用:先
next(),再remove(),不能跳步 - 每个
next()最多调一次remove(),重复调会抛IllegalStateException - 示例:
Iterator<String> it = list.iterator();<br>while (it.hasNext()) {<br> String s = it.next();<br> if (s.isEmpty()) it.remove(); // ✅ 安全 - 注意:
LinkedList的Iterator.remove()是 O(1),ArrayList是 O(n),删得多要考虑性能
想批量删?用 removeIf() 更简洁且底层也是安全的
JDK 8+ 的 Collection.removeIf() 内部封装了迭代器逻辑,语义清晰,且避免手写 while 循环出错。它本质上就是帮你做了上一条里的事,只是更短。
立即学习“Java免费学习笔记(深入)”;
- 等价于手动迭代 +
it.remove(),但不用暴露迭代器细节 - 支持所有实现了
Collection的类(ArrayList、HashSet、LinkedHashSet等) - 示例:
list.removeIf(String::isEmpty); // ✅ 安全,一行搞定
- 注意:Lambda 里不能修改集合本身(比如再调
list.add()),否则仍会触发异常
真要边遍历边增/改?换容器或预收集操作
ConcurrentModificationException 只拦「结构修改」(增删元素),不拦改值(比如 list.set(i, "x"))。但如果你确实需要在遍历时插入新元素,或者一边读一边建新集合,就得换思路:
- 改值可用索引循环:
for (int i = 0; i < list.size(); i++) {<br> list.set(i, list.get(i).trim()); // ✅ 允许 - 新增元素?别在原集合上加,先用
ArrayList或LinkedList收集待添加项,遍历完再addAll() - 并发场景?考虑
CopyOnWriteArrayList(读多写少)、ConcurrentHashMap,但它们有自己开销和语义限制(比如COWAL的迭代器看不到实时更新) - 最稳方案:用
stream().filter().collect()生成新集合,原集合只读
最容易被忽略的是:这个异常不是 bug 提示,而是设计契约。它逼你明确「遍历」和「修改」的关系——要么用配套接口,要么分两步走。越早接受这点,越少掉坑里。










