java在遍歷map的時候對元素進行移除出現的問題分析

在日常的開發中,我們經常需要對map,list等容器進行移除,但是處理不小心就會拋出ConcurrentModificationException異常,這到底是什麼原因造成的以及如何避免?這個本博文分析的重點。

首先看一下這個map的遍歷程序

package test1;

import java.util.HashMap;
import java.util.Map;

public class test {
    public static void main(String[] args){
        Map<Integer,Integer> map=new HashMap<>();
        //初始化map
        for(int i=0;i<10;i++){
            map.put(i,i);
        }
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            if(entry.getKey()==1){
                map.remove(entry.getKey());
            }
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
    }
}

運行的時候會拋出ConcurrentModificationException:

這是什麼原因造成的吶?下面我們來分析一下源碼

首先HashMap裏面有個成員屬性,如下圖

這個成員屬性是記錄map一共被修改的次數的,也就是對map進行put,remove的時候,這個modCount也是需要增加次數的。

而當我們使用迭代器Iterator對map進行遍歷的時候,在迭代器創建的時候就將這個值賦予給了迭代器裏面的一個成員變量expectedModCount。

然後我們再來看看map.remove()方法發生了什麼?

問題就出在這裏,map.remove()方法只會修改modCount成員變量,執行一次remove()方法後modCount變量加1,而expectedModCount還是原來的值。

因此在下一次迭代器執行next()方法的時候 ,就會因爲modeCount和expectedModCount這兩個不一致而拋出錯誤。

所以總結一下,凡是用迭代器遍歷並用map.remove()移除的都會出現這個問題。

那麼,日常開發中,我們想在迭代中移除元素,那該怎麼辦呢?解決辦法很簡單,代碼修改如下:

package test1;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class test {
    public static void main(String[] args){
        Map<Integer,Integer> map=new HashMap<>();
        //初始化map
        for(int i=0;i<10;i++){
            map.put(i,i);
        }
        Iterator<Map.Entry<Integer,Integer>> iterator=map.entrySet().iterator();
        while (iterator.hasNext()){
            int key=iterator.next().getKey();
            int value=iterator.next().getValue();
            if(key==1){
                iterator.remove();
            }
            System.out.println(key+" "+value);
        }
    }
}

只要用迭代器中的remove()方法刪除元素就行了,那我們再來看看迭代器中的remove()方法做了什麼。

 可以看到每一次remove,都會expectedModCount同步一下modCount,這就不會造成兩者數值不相等啦。

重點來了,那爲什麼java要這樣設計呢?是不是大牛們吃飽了撐着了呢?想什麼吶,肯定不是噶

 原因如下:HashMap和keySet的remove方法都可以通過傳遞key參數刪除任意的元素,而iterator只能刪除當前元素(current),一

旦刪除的元素是iterator對象中next所正在引用的,如果沒有通過modCount、 expectedModCount的比較實現快速失敗拋出異

常,下次循環該元素將成爲current指向,此時iterator就遍歷了一個已移除的過期數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章