1. 程式人生 > >[Java基礎要義]HashMap、LinkedHashMap元素遍歷機制探討

[Java基礎要義]HashMap、LinkedHashMap元素遍歷機制探討

      Map作為鍵值對Entry<K,V>的的容器,對其內部 鍵值對Entry<K,V> 的遍歷總歸是要有一個順序的。

      本文重點討論HashMap及其子類LinkedHashMap的遍歷機制,總結出兩者的特點和適用情況。

 CSDN-2014部落格之星投票啦

CSDN-2014部落格之星   評選開始啦,如果您覺得我的文章對您有所幫助,請您點選左邊欄的圖片投我一票,您的支援是我分享知識的強大動力!


1.HashMap的遍歷機制

           HashMap 提供了兩個遍歷訪問其內部元素Entry<k,v>的介面:

              1.       Set<Map.Entry<K,V>> entrySet() 

    返回此對映所包含的對映關係的 Set 檢視。

             2.       Set<K> keySet()              返回此對映中所包含的鍵的 Set 檢視。

     實際上,第二個藉口表示的Key的順序,和第一個介面返回的Entry順序是對應的,也就是說:這兩種介面對HashMap的元素遍歷的順序相相同的。  那麼,HashMap遍歷內部Entry<K,V> 的順序是什麼呢? 搞清楚這個問題,先要知道其內部結構是怎樣的。          

           HashMap內部對鍵值對的儲存結構使用的是陣列+連結串列的形式。其結構如下圖所示:


HashMap內部Entry<K,V>的遍歷順序:

          對Entry[] table 陣列,從index=0開始,依次遍歷table[i] 上的連結串列上的Entry物件。

由於HashMap在儲存Entry物件的時候,是根據Key的hash值判定儲存到Entry[] table陣列的哪一個索引值表示的連結串列上,所以籠統地說就是:使用hashMap.put(Key key,Value value)會將 對應的Entry<Key,Value>物件隨機地分配到某個Entry[] table陣列的元素表示的連結串列上。換一句話說就是:

     對HashMap遍歷Entry物件的順序和Entry物件的儲存順序之間沒有任何關係。


但是,我們有時候想要遍歷HashMap的元素Entry的順序和其儲存的順序一致,HashMap顯然不能滿足條件了。而LinkedHashMap則可以滿足這個需要。

2. LinkedHashMap 的遍歷機制

          LinkedHashMap 是HashMap的子類,它可以實現對容器內Entry的儲存順序和對Entry的遍歷順序保持一致。

       為了實現這個功能,LinkedHashMap內部使用了一個Entry型別的雙向連結串列用這個雙向連結串列記錄Entry的儲存順序。當需要對該Map進行遍歷的時候,實際上是遍歷的是這個雙向連結串列。

        LinkedHashMap內部使用的LinkedHashMap.Entry類繼承自 Map.Ent ry類,在其基礎上增加了LinkedHashMap.Entry型別的兩個欄位,用來引用該Entry在雙向連結串列中的前面的Entry物件和後面的Entry物件。

       它的內部會在 Map.Entry 類的基礎上,增加兩個Entry型別的引用:before,after。LinkedHashMap使用一個雙向連表,將其內部所有的Entry串起來。


            我們將通過以下例子,來了解內部雙向連結串列是怎樣構造的:

        LinkedHashMap linkedHashMap = new LinkedHashMap();
        linkedHashMap.put("name","louis");
        linkedHashMap.put("age","24");
        linkedHashMap.put("sex","male");

上述的程式碼除了會將對應的Entry物件放置到在Entry[] table 表示的陣列連結串列中外,還會將該Entry物件新增到其內部維護的雙向連結串列中。對應的LinkedHashMap內部的雙向連結串列變化如下:

對LinkedHashMap進行遍歷的策略:

           從 header.after 指向的Entry物件開始,然後一直沿著此連結串列 遍歷下去,直到某個entry.after == header 為止,完成遍歷。

由此,就可以保證遍歷LinkedHashMap內元素的順序,就是Entry插入到LinkedHashMap中的順序。

將上面程式碼中定義的linkedHashMap 遍歷輸出,會發現遍歷的順序跟插入的順序完全一致:

        Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();

        while(iterator.hasNext())
        {
            Map.Entry entry = iterator.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
結果輸出:


根據Entry<K,V>插入LinkedHashMap的順序進行遍歷的方式叫做:按插入順序遍歷

另外,LinkedHashMap還支援一種遍歷順序,叫做:Get讀取順序

     如果LinkedHashMap的這個Get讀取遍歷順序開啟,那麼,當我們在LinkedHashMap上呼叫get(key) 方法時,會導致內部 key對應的Entry在雙向連結串列中的位置移動到雙向連結串列的最後。

比如,如果當前LinkedHashMap內部的雙向連結串列的情況如下:

相關程式碼如下:

        //預設情況下LinkedHashMap的遍歷模式是插入模式,如果想顯式地指定為get讀取模式,那麼要將
        //其構造方法的引數置為true,(false 表示的是插入模式)
        LinkedHashMap linkedHashMap = new LinkedHashMap(16, (float) 0.75,true);

        linkedHashMap.put("name","louis");
        linkedHashMap.put("age","24");
        linkedHashMap.put("sex","male");
        linkedHashMap.get("name");//get()方法呼叫,導致對應的entry移動到雙向連結串列的最後位置

        Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();

        while(iterator.hasNext())
        {
            Map.Entry entry = iterator.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }


3. 總結

1.HashMap對元素的遍歷順序跟Entry插入的順序無關,而LinkedHashMap對元素的遍歷順序可以跟Entry<K,V>插入的順序保持一致。

2.當LinkedHashMap處於Get獲取順序遍歷模式下,當執行get() 操作時,會將對應的Entry<k,v>移到遍歷的最後位置。

3.LinkedHashMap處於按插入順序遍歷的模式下,如果新插入的<key,value> 對應的key已經存在,對應的Entry在遍歷順序中的位置並不會改變。

4. 除了遍歷順序外,其他特性HashMap和LinkedHashMap基本相同。