concurrentmodificationexception源于迭代器检测到集合结构被意外修改而触发fail-fast机制;单线程应使用iterator.remove()安全删除,多线程优先选用copyonwritearraylist或concurrenthashmap,通用兜底用延迟删除(先收集后removeall)。

ConcurrentModificationException 的核心原因是迭代器检测到集合结构被意外修改,触发了 fail-fast 机制。解决的关键在于:**不破坏迭代器对集合状态的预期一致性**。方法要按使用场景区分,单线程和多线程策略完全不同。
单线程中安全删除元素
增强 for 循环或普通 for 循环里直接调用 list.remove() 或 list.add() 都会出错。唯一安全的遍历中删除方式是使用迭代器自身的 remove() 方法:
- 它会在删除后同步更新内部的
expectedModCount,避免校验失败 - 必须在调用
next()之后、且仅调用一次remove(),否则抛IllegalStateException - 不能用于添加元素——Iterator 不提供 add();如需插入,改用普通 for 循环 + 索引操作
多线程环境下的推荐方案
多个线程同时读写同一集合时,仅靠 Iterator.remove 无法解决竞争问题。应优先选用并发集合:
- CopyOnWriteArrayList:适合读远多于写的场景(如监听器列表),每次写操作复制整个数组,迭代时完全无锁、不会抛异常
- ConcurrentHashMap:高并发 Map 场景首选,支持分段锁或 CAS,迭代器弱一致性(不抛 CME,但可能看不到最新写入)
- 慎用 synchronized 包裹整个遍历块:会严重降低并发吞吐,仅作为临时兼容方案
通用兜底策略:延迟删除
当无法改用并发集合、又不能用 Iterator.remove(比如需要根据复杂条件批量删、或需在 foreach 中处理)时,可先收集再清理:
- 遍历原集合,把待删元素加入临时集合(如
new ArrayList()) - 遍历结束后,调用
list.removeAll(toRemove) - 注意:若元素不可重写 equals/hashCode(如含随机字段的对象),需存索引而非对象本身
其他实用技巧
Java 8+ 提供了更简洁的替代写法,底层仍基于安全机制:
-
list.removeIf(predicate):内部使用 Iterator.remove,一行代替手动循环 -
list.replaceAll(unaryOperator):安全替换元素值(不改变结构,不触发 CME) - 对只读遍历场景,可考虑
Collections.unmodifiableList()提前防御误修改











