在Java中循环遍历List有三种方式:for循环、增强for循环(也就是常说的foreach循环)和iterator遍历。下面就这三种方式尝试移除List中的元素,看看是否存在问题。
List<String> list = new ArrayList<>(); list.add("yanggb"); list.add("yanggb1"); list.add("yanggb2"); list.add("yanggb2"); list.add("yanggb3");
1.for循环遍历List
for (int i = 0; i < list.size(); i++) { if ("yanggb2".equals(list.get(i))) { list.remove(i); } }
这种方式的问题在于,删除某个元素之后,因为List的大小发生了变化,List中元素的下标索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是原本的第3个元素。因为可能会导致漏删元素的问题,这种方式只能用于删除特定的一个元素的场景,但不适合循环删除多个元素的场景中使用。
具体地说,因为集合的大小是动态变化的,当你删除第一个要删除的元素之后,元素中的序号又重新排列,原来第二个要删除的元素在List中的下标往前移动了一位,如果这个第二个要删除的元素排在第一个要删除的元素之后,那么当循环的下标往后推一位之后,这个第二个要删除的元素永远不会被删除,因为循环不到这个元素了。。。究其原因是因为你要删除的元素往前面移动了,而你的i保存的值依旧往后走,所以如果让i不往后走,往前走一个,即可删除本来排在第二个位置的元素现在排在了第一个位置上的元素,这个就是解决这个问题的一个方法:
for (int i = 0; i < list.size(); i++) { list.remove(i); i--; // 动态前移循环中List的下标 }
另外的一个解决方法是倒序进行循环,这样的话删除元素就不会影响循环中的下标与List中元素原本下标的对应关系,也就不会出现元素删除漏了的问题。
for (int i = list.size() - 1; i >= 0; i--) { list.remove(i); }
2.增强for循环遍历List
for (String yanggb : list) { if ("yanggb2".equals(yanggb)) { list.remove(yanggb); } }
这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是如果在删除一个元素之后马上使用break跳出,则不会触发报错,因此这种方式只适用于只删除一个元素的场景。通过查看源代码可以发现,报错的原因在于在checkForComodification()方法中如果modCount变量不等于expectedModCount变量,就会抛出ConcurrentModificationException异常。
关于ConcurrentModificationException的具体原因及解决参考:https://www.cnblogs.com/dolphin0520/p/3933551.html#undefined
3.Iterator遍历
Iterator<String> it = list.iterator(); while (it.hasNext()){ String yanggb = it.next(); if ("yanggb2".equals(yanggb)){ it.remove(); } }
这种方式可以正常地进行循环及删除。但要注意的一点是,这里使用的是Iterator的remove()方法,如果用List的remove()方法同样会报上面提到的ConcurrentModificationException错误。
"人生好像就是那么的变化多端,你从来都没法肯定一件事情以后会不会出现什么出人意料的转折。"