在Java中从集合中删除元素:如何避免ConcurrentModificationException

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

“我想在循环中删除列表中的元素——结果遇到了ConcurrentModificationException”。熟悉吗?

\n

如果你曾经用Java编程,并试图在迭代过程中(例如在for-each循环中)从ArrayList或HashMap中删除元素,那么你很可能见过这个可怕的红色异常。它总是在最不合时宜的时候出现,仿佛Java在对你说:“嘿,你在干什么?我在遍历集合的时候,不准碰它!”

\n\n

在Stack Overflow上,这个问题获得了数千次浏览。这并非偶然:问题看似简单,但背后隐藏着一整套优化和多线程的细微差别。在本文中,我们将深入探讨如何在Java循环中正确地从集合中删除对象,既不引发ConcurrentModificationException,又不损失性能。

\n\n

开始吧!

\n\n

为什么会发生ConcurrentModificationException?

\n

简短的回答是:快速失败(fail-fast)迭代器。Java Collections Framework中的大多数集合(ArrayList、HashSet、HashMap等)都使用一个内部修改计数器(modCount)。当你创建一个迭代器时,它会记录当前的modCount值。每次调用next()时,迭代器都会检查集合是否被外部修改(通过集合自身的add()或remove()方法)。如果计数器不一致,就会抛出异常。

\n\n

一个肯定会出错的代码示例:

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nfor (String item : list) {\n    if ("B".equals(item)) {\n        list.remove(item); // ConcurrentModificationException!\n    }\n}\n
\n

为什么?因为for-each隐式地使用了迭代器,而list.remove()直接修改了modCount,绕过了迭代器。迭代器不会容忍这种行为。

\n\n

方法#1:使用Iterator.remove()——经典方法

\n

最简单且正确的方法是显式地使用迭代器并调用其remove()方法。该方法会同步迭代器的内部计数器与集合。

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nIterator<String> iterator = list.iterator();\nwhile (iterator.hasNext()) {\n    String item = iterator.next();\n    if ("B".equals(item)) {\n        iterator.remove(); // 安全!\n    }\n}\nSystem.out.println(list); // [A, C, D]\n
\n

优点:安全,适用于所有支持迭代器的集合。缺点:略显冗长。对于简单的过滤,可以使用更现代的方法。

\n\n

方法#2:Collection.removeIf()——现代且优雅

\n

从Java 8开始,大多数集合都提供了removeIf(Predicate filter)方法。这是根据条件删除元素最简洁的方式。

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nlist.removeIf(item -> "B".equals(item));\nSystem.out.println(list); // [A, C, D]\n
\n

在内部,removeIf使用迭代器(或通过保留索引直接遍历),因此不会出现ConcurrentModificationException。对于简单情况,这是推荐的方法。

\n

性能:对于ArrayList,removeIf方法的时间复杂度为O(n),但执行效率很高——它通过一次遍历移动元素,而不是逐个删除(后者时间复杂度为O(n²))。

\n\n

方法#3:按索引反向遍历(适用于List)

\n

如果出于某种原因你无法使用迭代器(例如,你需要同时修改多个元素,或者你正在处理旧代码),你可以使用普通的for循环按索引反向遍历列表。

\n
List<String> list = new A                

博客

书籍推荐