一、简介
本篇文章总结一下另外一个很常见的错误:迭代的时候对集合对象进行删除操作的正确使用方式。
二、使用详解
如果遇到需要循环删除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元素的正确姿势。 明天圣诞节了,今晚平安夜,都吃苹果没,祝小伙伴们平安夜快乐呀。