Java集合遍历中删除元素应避免ConcurrentModificationException,推荐用Iterator的remove()方法;其次可用倒序for循环(仅List)或收集待删元素后批量删除;多线程场景才用CopyOnWriteArrayList等并发集合。

Java 中集合遍历中删除元素,最常见问题就是 ConcurrentModificationException(并发修改异常),本质是迭代器的快速失败(fail-fast)机制在起作用:当集合结构被修改(add/remove),但迭代器未感知时,就抛异常。这不是线程安全问题,而是单线程下误操作导致的。
用 Iterator 的 remove() 安全删除
这是最推荐、最直观的方式。Iterator 的 remove() 方法是唯一被允许在遍历中删除当前元素的合法途径,它会同步更新迭代器内部状态,避免校验失败。
- 必须在
next()之后调用,且每次只能删一次(即不能连续调两次remove()) - 不能用集合自身的
remove()(如list.remove(obj)),否则仍会触发 fail-fast - 适用于所有实现了
Iterator的集合(ArrayList、LinkedList、HashSet、HashMap 的 keySet/entrySet 等)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("toRemove".equals(s)) {
it.remove(); // ✅ 正确
}
}倒序 for 循环 + 索引删除(仅限 List)
对 ArrayList 或 LinkedList 等支持随机访问或尾部高效删除的 List,可改用传统 for 循环,从后往前遍历。这样删除元素不会影响尚未访问的索引位置,避免漏删或越界。
- 正向遍历时
remove(i)会导致后续元素前移,i+1 位置元素被跳过 - 倒序则无此问题,每次删的是当前索引,前面的索引不受影响
- 注意:该方式依赖索引,不适用于 Set、Map 等无序/无索引集合
for (int i = list.size() - 1; i >= 0; i--) {
if ("toRemove".equals(list.get(i))) {
list.remove(i); // ✅ 安全(仅限 List)
}
}收集待删元素,遍历结束后批量删除
先遍历集合,把要删的元素(或其索引)暂存到另一个容器(如临时 List),遍历完成后再统一调用 removeAll() 或逐个删除。逻辑清晰,适合复杂判断条件或多集合联动场景。
立即学习“Java免费学习笔记(深入)”;
- 适用于任意集合类型(List/Set/Map.values() 等)
- 内存开销略增(需额外存储),但代码可读性和健壮性高
- 注意:若用
removeAll(Collection),确保待删集合与原集合元素可正确 equals 比较
List<String> toRemove = new ArrayList<>();
for (String s : list) {
if (shouldRemove(s)) {
toRemove.add(s);
}
}
list.removeAll(toRemove); // ✅ 批量安全删除使用并发集合(仅限多线程真实并发场景)
如果确实存在多线程同时读写同一集合,应直接选用线程安全的集合类,如 CopyOnWriteArrayList(适合读多写少)、ConcurrentHashMap,它们本身规避了 fail-fast 机制,支持遍历中安全修改。
-
CopyOnWriteArrayList的迭代器基于快照,遍历时修改不影响当前迭代,但写操作开销大 -
ConcurrentHashMap的 keySet/entrySet 迭代器也支持边遍历边删除(通过remove()方法),但不支持边遍历边添加 - ⚠️ 单线程下滥用并发集合反而降低性能,不应为规避 fail-fast 而误用


















