[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串起來。

 

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

 

[java]  view plain  copy
 
 print?
  1. LinkedHashMap linkedHashMap = new LinkedHashMap();  
  2. linkedHashMap.put("name","louis");  
  3. linkedHashMap.put("age","24");  
  4. linkedHashMap.put("sex","male");  

 

上述的代碼除了會將對應的Entry對象放置到在Entry[] table 表示的數組鏈表中外,還會將該Entry對象添加到其內部維護的雙向鏈表中。對應的LinkedHashMap內部的雙向鏈表變化如下:

 

對LinkedHashMap進行遍歷的策略:

           從 header.after 指向的Entry對象開始,然后一直沿着此鏈表 遍歷下去,直到某個entry.after == header 為止,完成遍歷。

 

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

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

 

[java]  view plain  copy
 
 print?
  1. Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();  
  2.   
  3. while(iterator.hasNext())  
  4. {  
  5.     Map.Entry entry = iterator.next();  
  6.     System.out.println(entry.getKey()+":"+entry.getValue());  
  7. }  
結果輸出:

 

 

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

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

     如果LinkedHashMap的這個Get讀取遍歷順序開啟,那么,當我們在LinkedHashMap上調用get(key) 方法時,會導致內部 key對應的Entry在雙向鏈表中的位置移動到雙向鏈表的最后。

比如,如果當前LinkedHashMap內部的雙向鏈表的情況如下:

相關代碼如下:

 

[java]  view plain  copy
 
 print?
  1. //默認情況下LinkedHashMap的遍歷模式是插入模式,如果想顯式地指定為get讀取模式,那么要將  
  2. //其構造方法的參數置為true,(false 表示的是插入模式)  
  3. LinkedHashMap linkedHashMap = new LinkedHashMap(16, (float) 0.75,true);  
  4.   
  5. linkedHashMap.put("name","louis");  
  6. linkedHashMap.put("age","24");  
  7. linkedHashMap.put("sex","male");  
  8. linkedHashMap.get("name");//get()方法調用,導致對應的entry移動到雙向鏈表的最后位置  
  9.   
  10. Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();  
  11.   
  12. while(iterator.hasNext())  
  13. {  
  14.     Map.Entry entry = iterator.next();  
  15.     System.out.println(entry.getKey()+":"+entry.getValue());  
  16. }  


 

 

3. 總結

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

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

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM