forEach循环本身不引发ConcurrentModificationException,异常源于遍历中非迭代器方式修改集合结构;应避免在forEach中直接增删元素,改用Iterator.remove()、延迟操作或并发集合。

在 Java 中,forEach 循环本身不会直接导致并发修改异常(ConcurrentModificationException),真正触发该异常的是在遍历集合(如 ArrayList、HashMap)的同时,通过非迭代器方式(如 add()、remove())修改了集合结构。而 forEach 是基于迭代器实现的增强 for 循环或 Iterable.forEach() 方法,一旦底层集合被结构性修改,迭代器检测到 modCount 不一致,就会抛出异常。
避免在 forEach 中直接增删元素
这是最常见也最需规避的操作。例如:
错误示例:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.forEach(s -> {
if ("b".equals(s)) {
list.remove(s); // ❌ 触发 ConcurrentModificationException
}
});
正确做法:
- 改用
Iterator.remove()安全删除(仅限删除) - 收集待操作元素,遍历结束后统一处理(推荐用于增删)
- 使用支持并发修改的集合,如
CopyOnWriteArrayList(适合读多写少场景)
使用 Iterator 的 remove() 方法安全删除
这是标准、线程安全(单线程下)的删除方式:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s)) {
it.remove(); // ✅ 合法且不会抛异常
}
}
注意:Iterator.remove() 只能删除当前元素,且每调用一次 next() 后最多调用一次 remove();不能用于添加元素。
延迟修改:先收集后操作
适用于需要动态增删多个元素的场景,逻辑清晰且无风险:
- 遍历原集合,将要删除的元素存入临时集合(如
Set或List) - 遍历结束后调用
removeAll()或逐个remove() - 新增元素可直接在原集合上调用
add()(不在遍历中即可)
示例:
List<String> toRemove = new ArrayList<>();
list.forEach(s -> {
if (s.length() == 1) {
toRemove.add(s);
}
});
list.removeAll(toRemove); // ✅ 安全
考虑使用线程安全或不可变集合
若场景涉及多线程或频繁遍历+少量修改,可权衡替代方案:
-
CopyOnWriteArrayList:每次修改都复制底层数组,遍历时用旧副本,不抛CME;但写操作开销大,不适合高频修改 -
ConcurrentHashMap:键值对遍历与修改可并行(弱一致性),但不保证遍历过程看到最新修改 - 函数式风格:用
stream().filter().collect()生成新集合,原集合不变(推荐用于不可变语义)
例如:
List<String> filtered = list.stream()
.filter(s -> !"b".equals(s))
.collect(Collectors.toList()); // ✅ 原 list 未被修改

















