java中iterator遍历时最常抛出concurrentmodificationexception,主因是迭代中用非迭代器方法修改集合结构;安全做法是仅用iterator.remove()且需先调next(),禁用增强for循环内修改,多线程场景应选copyonwritearraylist或concurrenthashmap等线程安全集合。

Java 中 Iterator 遍历时最常抛出的异常是 ConcurrentModificationException,它不是因为“多线程”才发生,而是只要在迭代过程中被非迭代器方式修改了集合结构(比如用 list.remove() 或 list.add()),就会触发。防止它的核心是理解“谁有权改结构”和“怎么改才安全”。
只用 iterator.remove() 删除元素
这是最直接、最常用的规避方式。Iterator 的 remove() 方法会同步更新内部状态(如 expectedModCount),让下一次 hasNext() 检查通过。
- 必须先调用
next(),再调用remove();没调过next()就删,会抛IllegalStateException - 每个
next()后最多只能调一次remove();重复调也会抛IllegalStateException - 不能用集合自身的
remove()、add()、clear()等方法,哪怕是在单线程里
避免在增强 for 循环中修改集合
增强 for(for (String s : list))底层就是 Iterator,但它不暴露迭代器对象,无法调用 remove()。一旦在循环体里写 list.remove(s),下一轮 next() 必然失败。
- 如果只是过滤,优先用
Collection.removeIf(Predicate)(JDK 8+),它内部已封装了安全的迭代逻辑 - 如果需复杂处理(如替换、插入),改用
ListIterator,它支持set()和add() - 简单删除也可用倒序 for 循环:
for (int i = list.size()-1; i >= 0; i--),避免索引错位
多线程场景不能依赖 fail-fast
ConcurrentModificationException 是单线程“误操作”的预警,不是并发安全机制。多线程下它可能不抛,但数据已损坏。
- 读多写少:用
CopyOnWriteArrayList,迭代基于快照,不抛异常;但写操作开销大,不适用于高频修改 - 读写均衡:用
ConcurrentHashMap(注意它 keySet().iterator() 是弱一致的,不抛异常但不保证实时性) - 逻辑强一致性要求高:对集合加锁,确保所有读写(包括
iterator())都在同一把锁内执行
提前检查与防御性编程
虽然 hasNext() 本身不抛 NoSuchElementException,但 next() 会——所以必须配合使用,不能跳过 hasNext() 直接调 next()。
- 不要用 try-catch 包裹
next()来“兜底”,这属于设计错误;正确做法是严格遵循while(it.hasNext()) { it.next(); }模式 - 若需在遍历中做条件判断后删除,把判断逻辑放在
next()之后、remove()之前,结构清晰不易错 - 调试时留意 modCount 变化:可通过反射或日志观察集合修改是否意外触发了迭代器失效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











