搬砖系列如何在遍历List时安全删除集合元素
Posted christmad
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搬砖系列如何在遍历List时安全删除集合元素相关的知识,希望对你有一定的参考价值。
1 public void testIterRemove() { 2 List l1 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5)); 3 Iterator<Integer> iterator = l1.iterator(); 4 System.out.println("before=" + l1); 5 while (iterator.hasNext()) { 6 iterator.next(); 7 iterator.remove(); 8 } 9 System.out.println("after=" + l1); 10 } 11 /* 12 before=[1, 2, 3, 4, 5] 13 after=[] 14 */
代码中关键的部分在于:
iterator.next();
和
iterator.remove();
二者缺一不可。当然调用next()之前要调用hasNext()确保没有越界。
简单分析两个方法的代码即可得出答案:
首先看remove()方法,为什么hasNext() + remove()不可以呢:
1 public void remove() { 2 if (lastRet < 0) 3 throw new IllegalStateException(); 4 checkForComodification(); 5 6 try { 7 ArrayList.this.remove(lastRet); 8 cursor = lastRet; 9 lastRet = -1; 10 expectedModCount = modCount; 11 } catch (IndexOutOfBoundsException ex) { 12 throw new ConcurrentModificationException(); 13 } 14 }
由于代码用属性lastRet(即lastReturn index)控制异常,所以查看ArrayList#Itr的实例初始化语句,lastRet初值是-1,如果单独调用remove()会报错。
再看一下next()方法,就会找出答案:
1 public E next() { 2 checkForComodification(); 3 int i = cursor; 4 if (i >= size) 5 throw new NoSuchElementException(); 6 Object[] elementData = ArrayList.this.elementData; 7 if (i >= elementData.length) 8 throw new ConcurrentModificationException(); 9 cursor = i + 1; 10 return (E) elementData[lastRet = i]; 11 }
在方法中首先将cursor记录到变量 i,返回前将 i 的知赋给 lastRet ,完成lastRet的更新。方法返回值不是本次要考虑的范畴。
再回过头看remove()方法,有了lastRet后,就可以重置cursor。在方法中调用了ArrayList#remove(),这个方法会重新调整数组元素的位置,如果删掉的不是最后一个位置的元素(numMoved == 0 表示删掉的正好是最后一位元素),将会把index后面的元素往前移动一个,最后将elementData[--size] = null 为移除旧元素,帮助GC。
总结:
需要先调用next()更新lastRet的值,才能在remove()中通过第一关对lastRet的检查。因此在遍历List时需要联合使用 :
while( it.hasNext() ) { it.next(); it.remove(); }
以上是关于搬砖系列如何在遍历List时安全删除集合元素的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin集合操作总结 ( List 集合 | MutableList 集合 | List 集合遍历 | Set 集合 | MutableSet 集合 | Map 集合 | 可变 Map集合 )
Kotlin集合操作总结 ( List 集合 | MutableList 集合 | List 集合遍历 | Set 集合 | MutableSet 集合 | Map 集合 | 可变 Map集合 )