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<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
if ("b".equals(s)) list.remove(s); // 这里必抛异常
}
如果能稳定复现,就坐实了“遍历中直改集合”这一根因;若不能,则需检查是否涉及多线程、代理对象、或自定义集合类绕过了标准 fail-fast 机制。

















