跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何排查在 for-each 循环中动态修改集合导致 ConcurrentModificationException 的根源

for-each循环中直接调用集合的add()或remove()会触发ConcurrentModificationException,因Iterator的expectedModCount与集合modCount校验失败;结构性修改包括add、remove、clear等,而set元素内容安全。 直接在 for-each 循环里调用集合的 add() 或 remove() 方法,是触发
concurrentmodificationexception
最常见的根源。这不是并发问题,而是 java 集合的快速失败机制主动拦截了不安全的结构性修改。 看懂异常发生的底层逻辑 for-each 循环本质是调用集合的
iterator()
。Iterator 内部保存了一个
expectedModCount
,它在创建时等于集合当前的
modCount
(记录结构性修改次数)。每次调用
next()
或
hasNext()
前,都会校验两者是否一致。 你在循环中执行
list.remove(x)
→
modCount
+1,但
expectedModCount
没变 下一次迭代检查失败 → 立即抛出异常 结构性修改包括:
add()
、
remove()
、
clear()
、
retainAll()
、
removeAll()
,甚至
ArrayList
的扩容或
HashMap
的 rehash 快速定位问题代码段 异常堆栈通常指向 for-each 循环体内的某一行,重点关注该行是否直接调用了集合自身的增删方法: 检查所有
for (Type item : collection)
结构,逐行扫描是否有
collection.remove(...)
、
collection.add(...)
、
collection.clear()
注意隐式调用:比如在循环中调用了一个工具方法,而该方法内部又修改了正在遍历的集合 排查子列表(
subList()
)场景:对子列表增删也会同步影响原 list 的
modCount
,同样会触发异常 验证是否真为结构性修改而非内容变更 仅修改元素内容一般不会触发异常,例如:
list.set(i, newValue)
✅ 安全(非结构性)
list.get(i).setField("x")
✅ 安全(只改对象状态,没动集合结构)
list.remove(x)
❌ 不安全(结构性)
map.put(key, value)
❌ 在遍历时对 HashMap 执行可能触发 rehash,属于结构性修改 用最小复现确认根本原因 剥离业务逻辑,写一个极简测试片段:
ArrayList list = new ArrayList<>(Arrays.asList("a", "b", "c")); for (String s : list) { if ("b".equals(s)) list.remove(s); // 这里必抛异常 }
如果能稳定复现,就坐实了“遍历中直改集合”这一根因;若不能,则需检查是否涉及多线程、代理对象、或自定义集合类绕过了标准 fail-fast 机制。

相关文章