1. 程式人生 > >foreach中移除(remove)元素 問題彙總

foreach中移除(remove)元素 問題彙總

1,ArrayList 移除元素時,有時候報錯,有時候不報錯

List<String> a = new ArrayList<>();
        a.add("1");
        a.add("2");
        for (String temp : a) {
            if("1".equals(temp)){
                a.remove(temp);
            }
        }

上面程式碼在編譯和執行時都不會報錯,並且也能正常remove掉元素,但是如果將"1" 換成"2",執行時就會報ConcurrentModificationException.首選需要分析ArrayList的原始碼,ArrayList類繼承自AbstractList類,該類中有一個成員變數:

protected transient int modCount = 0;

modCount用於記錄變更次數,當add或者remove時,都會自增1

foreach時會呼叫ArrayList的iterator方法,該返回一個Itr型別的內部類,該內部類有幾個需要注意的成員變數:

int cursor;       // index of next element to return
int expectedModCount = modCount;

cursor用於記錄獲取到哪個元素,expectedModCount用於記錄變更次數,如果在iter過程中刪除了一個元素,在呼叫iter.next()時會呼叫 Itr.checkForComdification()方法校驗是否有變更:

final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

如果expectedModCount != modCount,就會報ConcurrentModificationException異常.

現在我們開始分析程式碼執行過程:

  1. 執行itr類的hasNext判斷是否還有元素,此時a的size(ArrayList的成員變數,用於記錄元素個數)是2,cursor是0,還有下一個,執行itr.next()
  2. itr.next()判斷expectedModCount 是否等於modCount,此時是等於的,繼續下一步
  3. 這個時候執行if判斷,滿足條件,呼叫arrayList的remove方法,此時modCount+1了,size -1(等於1了)和expectedModCount不等了
  4. 執行itr類的hasNext判斷是否還有元素,此時a的size是1,cursor是1,沒有下一個,迴圈結束

如果將"1",換成"2",我們繼續分析一下程式碼執行過程:

  1. 執行itr類的hasNext判斷是否還有元素,此時a的size是2(ArrayList的成員變數,用於記錄元素個數),cursor是0,還有下一個,執行itr.next()
  2. itr.next()判斷expectedModCount 是否等於modCount,此時是等於的,繼續下一步
  3. if判斷,不滿足,繼續下一次迴圈
  4. 執行itr類的hasNext判斷是否還有元素,此時a的size是2,cursor是1,還有下一個,執行itr.next()
  5. itr.next()判斷expectedModCount 是否等於modCount,此時是等於的,繼續下一步
  6. if判斷,滿足條件,開始移除元素,此時,modCount +1 ,size -1
  7. 執行itr類的hasNext判斷是否還有元素,此時a的size是1,cusor是2,還會再次執行一次itr.next()
  8. expectedModCount不等於modCount,報錯

2,HashSet 類remove元素時,不報錯也沒有一處成功

import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

public class HashCodeTest {
    private int hashCode = 0;

    @Override public int hashCode() {
        return hashCode ++;
    }

    public static void main(String[] args) {
        Set<HashCodeTest> set = new HashSet<HashCodeTest>();

        set.add(new HashCodeTest());
        System.out.println(set.size());
        for (Iterator<HashCodeTest> iter = set.iterator();
                iter.hasNext();) {
            iter.next();
            iter.remove();
        }
        System.out.println(set.size());
    }
}
上面程式碼在remove時,不報錯也沒有移除成功,原因是HashSet內部使用HashMap的key儲存資料,remove時會校驗key的hashCode和equals方法,如果發生過變化,則無法成功移除元素