如何在遍历集合时删除元素

2024-08-27 10:50:07 浏览数 (3)

概述

对java.util包下的集合类型来说,如果在通过for-each循环进行遍历时,对集合进行修改操作(删除、添加、修改元素),很多情况下会抛出ConcurrentModificationException异常[1]。这是因为for-each循环是通过迭代器的方式进行的遍历。而该包下的迭代器都属于fail-fast迭代器[2],即不允许在遍历的同时,对集合进行修改,因为这样会导致不确定的遍历结果。例如:

代码语言:java复制
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

for (String s : list) {
    if ("c".equals(s)) {
        list.remove(s);
    }
}

原理

集合内部有一个modCount属性,用于记录集合被修改的次数,而迭代器作为集合的内部类,有一个expectedModCount属性。每次迭代时都会实例化一个迭代器,实例化时expectedModCount等于modCount. 如果在迭代进行中,调用了集合的remove、add等方法,modCount就会增加,而迭代器中的expectedModCount仍然保持不变。而迭代中每次通过next方法获取下一个元素时,都会检查这两个值是否相等,如不相等就会抛出ConcurrentModificationException.

解决方案

不推荐在遍历的同时对集合进行修改,可新建一个集合,用于保存修改,而不是直接在原集合上修改。但若是出于代码简洁的目的,想要实现在遍历时删除某个元素,可通过以下方式实现:

1. 通过迭代器删除元素

代码语言:java复制
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
    String s = itr.next();
    if ("c".equals(s)) {
        itr.remove();
    }
}

2. 通过stream删除元素

代码语言:java复制
List<String> list = list.stream().filter(s -> !"c".equals(s)).collect(Collectors.toList());

3. 通过removeIf方法删除元素

代码语言:java复制
list.removeIf(s -> "c".equals(s));  //语法糖,removeIf本质是通过迭代器进行删除

[1]: 即便不抛异常,也无法保证遍历结果的准确性

[2]: fail-fast是一种通用的设计思想,指一旦检测到可能发生错误,就马上抛出异常,并终止程序执行

参考文献


https://www.baeldung.com/java-fail-safe-vs-fail-fast-iterator

https://developer.aliyun.com/article/1009062

0 人点赞