筆者最近在調試項目bug的時候,遇到了一個很奇怪的bug,就是在對hashmap集合進行遍歷的時候,同時作了remove操做,這個操做最後致使拋出了java.util.ConcurrentModificationException的錯誤。
帶着疑惑,下面參考着源碼,分析問題的緣由。
首先,重現問題,構造一個map並往裏面加元素:java
private static HashMap<Integer, String> map = new HashMap<Integer, String>();;
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
map.put(i, "value" + i);
}
}
複製代碼
而後移除一些元素,此時就會報java.util.ConcurrentModificationException錯誤bash
for(Map.Entry<Integer, String> entry : map.entrySet()){
Integer key = entry.getKey();
if(key % 2 == 0){
System.out.println("To delete key " + key);
map.remove(key);
System.out.println("The key " + + key + " was deleted");
}
複製代碼
從報錯中能夠看出,HashMap$HashIterator.nextNode這個方法有代碼錯誤了,點進去看,大概知道HashMap.this.modCount != this.expectedModCount 成立併發
再看一下hashmap的remove操做是作了什麼:高併發
這裏對modCount進行了自增操做,表示操做動做+1。再看modCount和expectedModCount是什麼東西this
能夠看出迭代器初始化的時候就對modCount和expectedModCount進行同步。
到此,能夠看出報錯的緣由:spa
那什麼狀況下在遍歷的時候能夠刪除map裏面的元素呢?看下迭代器提供的remove方法:線程
能夠看出迭代器裏remove了一個元素以後會對expectedModCount從新賦值,這樣再次遍歷的時候就不會報錯了。因此以前的代碼能夠改爲以下寫法,直接調用迭代器的remove方法。調試
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Integer, String> entry = it.next();
Integer key = entry.getKey();
if(key % 2 == 0){
System.out.println("To delete key " + key);
it.remove();
System.out.println("The key " + + key + " was deleted");
}
}
複製代碼
替換機制:code