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