ConcurrentModificationException源于Java集合的快速失败机制:迭代器通过modCount检测结构修改,不一致时抛出异常;正确做法是用Iterator.remove()、并发集合或removeIf等安全方式。

Java 集合遍历中,Iterator 本身不会主动“抛出异常”,但它在特定非法操作下会抛出 ConcurrentModificationException(并发修改异常)。这个异常不是由 Iterator 主动 throw 出来的,而是由集合的迭代器实现(如 ArrayList$Itr、HashMap$HashIterator 等)在检测到**结构被意外修改**时触发的。
为什么 Iterator 会抛 ConcurrentModificationException?
这是 Java 的“快速失败”(fail-fast)机制:迭代器内部维护一个 expectedModCount,它记录了创建迭代器时集合的修改次数(modCount)。每次调用 next() 或 hasNext() 前,都会检查当前 modCount 是否仍等于 expectedModCount。如果不等,说明集合在迭代过程中被其他代码(非当前迭代器)结构性修改过(如 add/remove 元素),于是立即抛出异常。
哪些操作会触发该异常?
- 在使用
for-each循环(本质是隐式 Iterator)或显式Iterator遍历时,直接调用集合自身的add()、remove()、clear()等方法 - 多个线程同时遍历并修改同一个非线程安全集合(如
ArrayList) - 在一个迭代器遍历过程中,用另一个迭代器(哪怕是同一个集合的新 Iterator)去修改集合
怎么安全地边遍历边删除元素?
唯一被 Iterator 明确支持的修改方式是调用 Iterator.remove() —— 它会同步更新 expectedModCount,避免异常。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
✅ 正确示例:
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("abc".equals(s)) {
it.remove(); // 安全!由迭代器自己控制
}
}</string>
❌ 错误示例:
for (String s : list) {
if ("abc".equals(s)) {
list.remove(s); // 抛 ConcurrentModificationException!
}
}
如何避免这个异常?
- 删除操作一律使用
Iterator.remove(),不要用集合方法 - 需要批量删除时,先收集待删元素,遍历结束后再调用
removeAll() - 多线程场景下,改用线程安全集合(如
CopyOnWriteArrayList、ConcurrentHashMap),它们的迭代器不抛此异常(但可能看到弱一致性视图) - 若确定单线程且需频繁增删,可考虑先转为数组或使用
removeIf()(JDK 8+,内部已做安全处理)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










