foreach中移除(remove)元素 問題彙總
阿新 • • 發佈:2019-01-30
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異常.
現在我們開始分析程式碼執行過程:
- 執行itr類的hasNext判斷是否還有元素,此時a的size(ArrayList的成員變數,用於記錄元素個數)是2,cursor是0,還有下一個,執行itr.next()
- itr.next()判斷expectedModCount 是否等於modCount,此時是等於的,繼續下一步
- 這個時候執行if判斷,滿足條件,呼叫arrayList的remove方法,此時modCount+1了,size -1(等於1了)和expectedModCount不等了
- 執行itr類的hasNext判斷是否還有元素,此時a的size是1,cursor是1,沒有下一個,迴圈結束
如果將"1",換成"2",我們繼續分析一下程式碼執行過程:
- 執行itr類的hasNext判斷是否還有元素,此時a的size是2(ArrayList的成員變數,用於記錄元素個數),cursor是0,還有下一個,執行itr.next()
- itr.next()判斷expectedModCount 是否等於modCount,此時是等於的,繼續下一步
- if判斷,不滿足,繼續下一次迴圈
- 執行itr類的hasNext判斷是否還有元素,此時a的size是2,cursor是1,還有下一個,執行itr.next()
- itr.next()判斷expectedModCount 是否等於modCount,此時是等於的,繼續下一步
- if判斷,滿足條件,開始移除元素,此時,modCount +1 ,size -1
- 執行itr類的hasNext判斷是否還有元素,此時a的size是1,cusor是2,還會再次執行一次itr.next()
- 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方法,如果發生過變化,則無法成功移除元素