1. 程式人生 > >【搬磚系列】如何在遍歷List時安全刪除集合元素

【搬磚系列】如何在遍歷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();
}