集合的遍历方式有三种,分别对应着三种删除元素的情况:
1、利用for循环
List<String> list=new ArrayList<>();
list.add("1111");
list.add("2222");
list.add("3333");
for (int i=0;i<list.size();i++){
if (list.get(i).length()==4){
list.remove(i);
}
}
for (String i:list) {
System.out.println(i);
}
上面的执行结果是2222,这也是这种方式存在的问题,这种遍历方式只支持删除一个元素,如果删除多个元素,就会出错。因为在删除某个元素后,该元素后面的元素会自己向前移动,这时当前索引i已经指向了下一个元素,但是在下一次循环中,索引i又执行了加一操作,所以有一个元素被“跨过”了。比如说删除了第一个元素,后面的元素前移,当前索引已经指向了第二个元素,所以下一次循环中,访问到的其实是第三个元素,第二个元素就没有被访问到。所以要想正常使用,需要在if语句最后加上break,保证可以正确删除一个元素后退出循环。
2、利用foreach
List<String> list=new ArrayList<>();
list.add("1111");
list.add("2222");
list.add("3333");
for(String i:list){
if(i.length()==4){
list.remove(i);
break;
}
}
for (String i:list) {
System.out.println(i);
}
上面的代码的执行结果是2222,3333。可以看到这一条和第一条是很像的,但是在if语句中有一个break语句,如果没有break语句,在运行时,就会抛出ConcurrentModificationException异常,所以这种方式只能用于删除一个元素。
那么为什么会抛出这种异常呢?去掉break,查看反编译代码,发现foreach的实现其实是用到了迭代器,如下
List<String> list = new ArrayList();
list.add("1111");
list.add("2222");
list.add("3333");
Iterator var2 = list.iterator();
String i;
while(var2.hasNext()) {
i = (String)var2.next();
if (i.length() == 4) {
list.remove(i);
}
}
要想了解为什么会抛出这个异常,我们需要先了解迭代器的工作原理:迭代器其实就是集合的一个“快照”,他保存的是集合的结构,但是要求迭代器必须是最新的,也就是说,在获取迭代器后,如果集合的结构发生了改变,呢么这个迭代器就作废了,要想继续使用,只能重新获取一个构造器,否则就会抛出上面的异常。介绍了迭代器的工作原理,也就引出了第三种遍历方式:利用迭代器遍历
3、利用迭代器遍历
List<String> list=new ArrayList<>();
list.add("1111");
list.add("2222");
list.add("3333");
Iterator<String> it = list.iterator();
while (it.hasNext()){
if(it.next().length()==4){
it.remove();
}
}
for (String i:list) {
System.out.println(i);
}
上面代码的执行结果是什么也没输出,正确!
上面说到,如果集合的结构发生了改变,与迭代器结构不同,就需要重新获取迭代器。集合结构与迭代器不同,是因为使用了集合的remove方法,该方法只会改变集合的结构,而不改变迭代器的结构。如果我们使用迭代器的remove方法,他会在迭代器中和集合中同时删除这个元素,所以两者的结构还是相同的,不会抛出异常,结果也不会出现错误。所以,如果要删除多个元素,就需要使用这种方式。