迭代时移除List中的元素的正确方式

一、简介

本篇文章总结一下另外一个很常见的错误:迭代的时候对集合对象进行删除操作的正确使用方式。

二、使用详解

如果遇到需要循环删除List中匹配的元素,相信很多小伙伴第一个想到的办法就是使用for循环遍历,然后使用挨个比较,如果相等则删除元素,即如下面的代码所示:

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (int i = 0; i < list.size(); i++) {
    list.remove(i);
}
//[b, d]
System.out.println(list);

上面的代码应该是经常见到的,我们运行程序,发现结果输出为:

[b, d]

惊不惊喜意不意外,list居然没有全部删除掉,其实很多小伙伴们都犯过这种错误,为什么会出现这种情况呢?

当一个元素被移除时,该List的大小(size)就会缩减,同时也改变了索引的指向,也就是上面的代码只会循环两次,长度在不断减少,第一次循环0 < 4 ,第二次循环 1 < 3 ,不满足下一次循环条件 2 < 2,故只有两次循环就结束。所以,在迭代的过程中使用索引,将无法从List中正确地删除多个指定的元素。

那既然for循环不行,那我们换成foreach试试,代码如下:

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (String str : list) {
    if ("a".equals(str)) {
        list.remove(str);
    }
}
System.out.println(list);

运行代码,很遗憾,报错了,如图:

麻蛋,为什么呢?

其实,在 foreach循环中,编译器使得 remove()方法先于next()方法被调用,这就导致了ConcurrentModificationException 异常,我们debug源码看一下,在下图位置抛出异常:

 

由此我们知道,正常情况下,next()方法必须在remove()方法之前被调用,这样才能正确移除元素,我们优化代码如下:

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String next = iterator.next();
    if ("a".equals(next)) {
        iterator.remove();
    }
}
//[b, c, d]
System.out.println(list);

所以,这才是在循环中移除List元素的正确姿势。 明天圣诞节了,今晚平安夜,都吃苹果没,祝小伙伴们平安夜快乐呀。

发布了197 篇原创文章 · 获赞 86 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/Weixiaohuai/article/details/103689123