concurrentmodificationexception本质是fail-fast机制拦截遍历与修改职责错位,单线程高频发生;排查需追溯调用栈中next()/hasnext()前的结构修改操作,修复应选用removeif、显式iterator.remove()、倒序for或批量处理四类方案。

Java 中集合迭代失效异常(ConcurrentModificationException)不是线程安全问题的专属信号,它在单线程下高频出现,本质是 fail-fast 机制对“遍历与修改职责错位”的主动拦截。排查要盯住调用栈中的 next() 或 hasNext(),修复关键在于让修改动作回归迭代器语义或彻底隔离读写阶段。
看堆栈:一眼锁定真实出问题的位置
异常堆栈通常显示报错在 iterator.next(),但真正破坏一致性的是它之前某次对集合的 remove()、add() 或 clear() 调用。不要只盯着报错行,往上翻两层,找最近一次直接操作集合结构的方法调用。
- 如果堆栈里有
for (String s : list),基本可断定是增强 for 中调了list.remove() - 如果看到
it.next()后紧跟list.remove(...),说明用了错误的删除主体 - 多个迭代器共用一个集合时,只要其中一个改了结构,其余迭代器下次调
next()就崩
改代码:四类安全方案按场景选用
修复不靠 try-catch,而靠切换操作范式。根据是否需条件判断、是否多线程、是否 List 类型,选最匹配的一种:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
-
单条件删除首选
removeIf():一行解决,语义清晰,JDK 8+ 原生支持list.removeIf(s -> s.startsWith("tmp")); -
复杂逻辑或需访问元素状态,用显式 Iterator:必须调
it.remove(),且只能在next()后立即调用一次Iterator<string> it = list.iterator(); while (it.hasNext()) { String s = it.next(); if (needRemove(s)) it.remove(); }</string> -
仅限 List,且删除逻辑简单,可用倒序 for 循环:避开索引偏移,也不触发迭代器校验
for (int i = list.size() - 1; i >= 0; i--) { if (list.get(i).length() == 0) list.remove(i); } -
需增删多个元素,或操作后还要继续用原集合,先收集再批量处理:用临时集合暂存目标,退出遍历后再统一删
List<string> toRemove = new ArrayList(); for (String s : list) if (s.contains("bad")) toRemove.add(s); list.removeAll(toRemove);</string>
避坑细节:这些地方一错就白改
即使选对方案,忽略以下细节仍会触发异常或引发逻辑错误:
-
Iterator.remove()必须在next()之后立刻调用,且两次next()之间最多调一次remove(),否则抛IllegalStateException -
removeIf()的 lambda 体内不能再调用集合的任何结构性方法(如add()),否则仍触发ConcurrentModificationException -
CopyOnWriteArrayList虽不抛异常,但迭代器看到的是快照,删完别人看不到;写操作复制整个数组,不适合频繁修改场景 - 普通 for 正向循环中调
list.remove(i),会导致后续元素前移、当前索引跳过下一个元素——这不是异常,而是静默逻辑错误
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










