编程语言只是一种工具,它不应该成为我们技术前进之路上的壁垒。

【搬砖系列】如何在遍历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         }
ArrayList#Itr.remove()

由于代码用属性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         }
ArrayList#Itr.next()

在方法中首先将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();
} 

 

同理,其他集合删除应该也是这个步骤,没来得及验证,欢迎评论区小锤换大锤:)。

posted on 2018-11-01 23:28  独角没有戏  阅读(518)  评论(0编辑  收藏  举报

导航