for循环用了这么多次,你还不懂吗?
❝ ❝当我们自信满满的在
❞ ❞for
循环中add/remove
时,常常会出现一些意想不到的结果。
普通for循环中删除
List testList = Lists.newArrayList("one", "two", "three", "four", "five", "six");// for => 无异常但结果错误for (int i = 0; i < testList.size(); i++) { String s = testList.get(i); if("four".equals(s)) { testList.remove(i); } else { System.out.println(testList.get(i)); }}
运行结果:
one
twothreesix
可以发现显然不是我们想要的结果,那为什么删除"four","five"也跟着消失了呢?画了一张图帮助大家理解:
「原因:」 当for循环遍历到"four"时,i = 3,这时删除元素"four",n++(n此时为4) 进入下次循环,由于"four"被删除,"four"后面的元素向前移动,此时"five"和"six"的下标分别为3、4,所以程序跳过了"five",直接打印了"six"。如果我们在remove元素的同时将下标 n 减 1 或者从列表尾部往头部遍历即可解决问题,如下代码所示:
List testList = Lists.newArrayList("one", "two", "three", "four", "five", "six");// remove元素的同时将下标 n 减 1for (int i = 0; i < testList.size(); i++) { String s = testList.get(i); if("four".equals(s)) { testList.remove(i); i--; } else { System.out.println(testList.get(i)); }}// 从列表尾部往头部遍历for (int i = testList.size() - 1; i >= 0; i--) { String s = testList.get(i); if("four".equals(s)) { testList.remove(i); } else { System.out.println(testList.get(i)); }}
增强for循环中删除
List testList = Lists.newArrayList("one", "two", "three", "four", "five", "six");// for-each => Exception in thread "main" java.util.ConcurrentModificationExceptionint n = 0;for (String s : testList) { if(n++ == 3) { testList.remove(n); }}
运行结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at com.ywh.test.TestRemove.main(TestRemove.java:30)
「原因:」 foreach
方式遍历元素的时候,是生成iterator,然后使用iterator遍历。在生成iterator的时候,会保存一个expectedModCount
参数,这个是生成iterator的时候List中修改元素的次数。如果你在遍历过程中删除元素,List中modCount
就会变化,如果这个modCount
和exceptedModCount
不一致,就会抛出异常。这个是为了安全的考虑。如果使用iterator遍历过程中,使用List修改了元素,可能会出现不正常的现象。如果使用iterator的remove方法则会正常,因为iterator的remove方法会在内部调用List的remove方法,但是会修改excepedModCount
的值,因此会正常运行,源码如下所示:
public void remove() {
if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); }}
使用迭代器remove代码如下所示:
Iterator iterator = testList.iterator();while (iterator.hasNext()) { String next = iterator.next(); if(next.equals("four")) { iterator.remove(); }}
点击关注不迷路更多技术分享、优质文章尽在作者的公众号:「程序员的小黑屋」,长按下图订阅,第一时间获取更新。↓↓↓