java數據結構-HashMap

<a target=_blank id="viewpost1_TitleUrl" href="http://www.blogjava.net/dongbule/archive/2011/02/15/344387.html">java數據結構-HashMap</a>	 <div class="postText">一直以來似乎都有一個錯覺,認爲map跟其他的集合類一樣繼承自Collection,其實不然,Map和Collection在結構層次上是沒有任何關係的,通過查看源碼可以發現map所有操作都是基於key-value對,而不是單獨的元素。

 下面以HashMap爲例子,深入對Map的實現機制進行了解,在這個過程中,請打開jdk源碼。

<strong>Hash算法</strong>

HashMap使用Hash算法,所以在解剖HashMap之間,需要先簡單的瞭解Hash算法,Hash算法一般也成爲散列算法,通過散列算法將任意的值轉化成固定的長度輸出,該輸出就是散列值,這是一種壓縮映射,也就是,散列值的空間遠遠小於輸入的值空間。
 簡單的說,hash算法的意義在於提供了一種快速存取數據的方法,它用一種算法建立鍵值與真實值之間的對應關係,(每一個真實值只能有一個鍵值,但是一個鍵值可以對應多個真實值),這樣可以快速在數組等裏面存取數據。

 下面我們建立一個HashMap,然後往裏面放入12對key-value,這個HashMap的默認數組長度爲16,我們的key分別存放在該數組的格子中,每個格子下面存放的元素又是以鏈表的方式存放元素。

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--
   <span style="color: rgb(0, 0, 255);">public<span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> main(String[] args) {
         Map map </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> HashMap();
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">What</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">You</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Don't</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Know</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">About</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Geo</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">APIs</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Can't</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Hurt</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">you</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">google</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">map</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">hello</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
     }</span>
當我們新添加一個元素時,首先我們通過Hash算法計算出這個元素的Hash值的hashcode,通過這個hashcode的值,我們就可以計算出這個新元素應該存放在這個hash表的哪個格子裏面,如果這個格子中已經存在元素,那麼就把新的元素加入到已經存在格子元素的鏈表中。

 運行上面的程序,我們對HashMap源碼進行一點修改,打印出每個key對象的hash值

What-->hash值:8
 You-->hash值:3
 Don't-->hash值:7
 Know-->hash值:13
 About-->hash值:11
 Geo-->hash值:12
 APIs-->hash值:1
 Can't-->hash值:7
 Hurt-->hash值:1
 you-->hash值:10
 google-->hash值:3
 map-->hash值:8
 hello-->hash值:0

計算出來的Hash值分別代表該key應該存放在Hash表中對應數字的格子中,如果該格子已經有元素存在,那麼該key就以鏈表的方式依次放入格子中

<img width="616" height="193" alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/jar/hashmap1.png" />

 從上表可以看出,Hash表是線性表和鏈表的綜合所得,根據數據結構的定義,可以得出粗劣的結論,Hash算法的存取速度要比數組差一些,但是比起單純的鏈表,在查找和存取方面卻要好多。

 如果要查找一個元素時,同樣的方式,通過Hash函數計算出這個元素的Hash值hashcode,然後通過這個hashcode值,直接找到跟這個hash值相對應的線性格子,進如該格子後,對這個格子存放的鏈表元素逐個進行比較,直到找到對應的hash值。

 在簡單瞭解完Hash算法後,我們打開HashMap源碼

<strong>初始化HashMap</strong>

下面我們看看Map map = new HashMap();這段代碼究竟做了什麼,發生了什麼數據結構的變化。

HashMap中幾個重要的屬性

transient Entry[] table;
用來保存key-value的對象Entry數組,也就是Hash表

transient int size;
返回HashMap的鍵值對個數

final float loadFactor;
負載因子,用來決定Entry數組是否擴容的因子,HashMap默認是0.75f

 int threshold;
重構因子,(capacity * load factor)負載因子與Entry[]數組容積的乘值

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> HashMap</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">
     </span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);"> AbstractMap</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">
     </span><span style="color: rgb(0, 0, 255);">implements</span><span style="color: rgb(0, 0, 0);"> Map</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">, Cloneable, Serializable
 {
     </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> threshold;    

     </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">float</span><span style="color: rgb(0, 0, 0);"> loadFactor;

     </span><span style="color: rgb(0, 0, 255);">transient</span><span style="color: rgb(0, 0, 0);"> Entry[] table;

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">float</span><span style="color: rgb(0, 0, 0);"> DEFAULT_LOAD_FACTOR </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0.75f</span><span style="color: rgb(0, 0, 0);">;

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> DEFAULT_INITIAL_CAPACITY </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">16</span><span style="color: rgb(0, 0, 0);">;

     </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> HashMap(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> initialCapacity, </span><span style="color: rgb(0, 0, 255);">float</span><span style="color: rgb(0, 0, 0);"> loadFactor) {
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (initialCapacity </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">)
             </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> IllegalArgumentException(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Illegal initial capacity: </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">
                                                initialCapacity);
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (initialCapacity </span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY)
             initialCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY;
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (loadFactor </span><span style="color: rgb(0, 0, 0);"><=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);"> Float.isNaN(loadFactor))
             </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> IllegalArgumentException(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Illegal load factor: </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">
                                                loadFactor);

         </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);"> Find a power of 2 >= initialCapacity</span><span style="color: rgb(0, 128, 0);">
</span><span style="color: rgb(0, 0, 0);">        </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> capacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">;
         </span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);"> (capacity </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> initialCapacity)
             capacity </span><span style="color: rgb(0, 0, 0);"><<=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">;

         </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.loadFactor </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> loadFactor;
         threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(capacity </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> loadFactor);
         table </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry[capacity];
         init();
     }</span></div>以public HashMap(int initialCapacity, float loadFactor)構造函數爲例,另外兩個構造函數實際上也是以同種方式來構建HashMap.

首先是要確定hashMap的初始化的長度,這裏使用的策略是循環查出一個大於initialCapacity的2的次方的數,例如initialCapacity的值是10,那麼大於10的數是2的4次方,也就是16,capacity的值被賦予了16,那麼實際上table數組的長度是16,之所以採用這樣的策略來構建Hash表的長度,是因爲2的次方運算對於計算機來說是有相當的效率。

loadFactor,被稱爲負載因子,HashMap的默認負載因子是0.75f

 threshold,接下來是重構因子,由負載因子和容量的乘機組成,它表示當HashMap元素被存放了多少個之後,需要對HashMap進行重構。

 通過這一系列的計算和定義後,初始化Entry[] table;

<strong>put(key,value)</strong>

接下來看一對key-value是如何被存放到HashMap中:put(key,value)

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> V put(K key, V value) {
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (key </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> putForNullKey(value);
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> hash(key.hashCode());
         
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> i </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> indexFor(hash, table.length);
         System.out.println(key</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">-->hash值:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">i);</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">這就是剛纔程序打印出來的key對應hash值</span><span style="color: rgb(0, 128, 0);">
</span><span style="color: rgb(0, 0, 0);">        </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);"> (Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table[i]; e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">; e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next) {
             Object k;
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e.hash </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">&&</span><span style="color: rgb(0, 0, 0);"> ((k </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.key) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> key </span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);"> key.equals(k))) {
                 V oldValue </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.value;
                 e.value </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> value;
                 e.recordAccess(</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">);
                 </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> oldValue;
             }
         }

         modCount</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">;
         addEntry(hash, key, value, i);
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
     }

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> h) {
         h </span><span style="color: rgb(0, 0, 0);">^=</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">12</span><span style="color: rgb(0, 0, 0);">);
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> h </span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">7</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">4</span><span style="color: rgb(0, 0, 0);">);
     }

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> indexFor(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> h, </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> length) {
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> h </span><span style="color: rgb(0, 0, 0);">&</span><span style="color: rgb(0, 0, 0);"> (length</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">);
     }</span></div>
這裏是整個hash的關鍵,請打開源碼查看一步一步查看。

<u>hash(key.hashCode())</u> 計算出key的hash碼 //對於hash()的算法,這裏有一篇分析很透徹的文章<<a target=_blank href="http://www.javaeye.com/topic/709945"></a><a target=_blank href="http://www.javaeye.com/topic/709945"></a><a target=_blank href="http://www.javaeye.com/topic/709945">HashMap hash方法分析</a>>
<u>indexFor(hash, table.length)</u> 通過一個與算法計算出來,該key應在存放在Hash表的哪個格子中。
<u>for (Entry<K,V> e = table[i]; e != null; e = e.next)</u> 然後再遍歷table[i]格中的鏈表,判斷是否已經存在一樣的key,如果存在一樣的key值,那麼就用新的value覆蓋舊的value,並把舊的value值返回。
<u>addEntry(hash, key, value, i)</u> 如果經過遍歷鏈表沒有發現同樣的key,那麼進行addEntry函數的操作,增加當前key到hash表中的第i個格子中的鏈表中

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> addEntry(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash, K key, V value, </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> bucketIndex) {
         Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table[bucketIndex];
         table[bucketIndex] </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">(hash, key, value, e);
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (size</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">>=</span><span style="color: rgb(0, 0, 0);"> threshold)
             resize(</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> table.length);
     }</span></div><u>
 Entry<K,V> e = table[bucketIndex];</u>  創建一個Entry對象來存放鍵值(ps:Entry對象是一個鏈表對象)
<u>table[bucketIndex] = new Entry<K,V>(hash, key, value, e);</u> 將Entry對象添加到鏈表中
<u>if (size++ >= threshold) resize(2 * table.length);</u> 最後將size進行自增,判斷size值是否大於重構因子,如果大於那麼就是用resize進行擴容重構。

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> resize(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> newCapacity) {
         Entry[] oldTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> oldCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> oldTable.length;
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (oldCapacity </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY) {
             threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Integer.MAX_VALUE;
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">;
         }

         Entry[] newTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry[newCapacity];
         transfer(newTable);
         table </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable;
         threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(newCapacity </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> loadFactor);
     }</span></div>
這裏爲什麼是否需要擴容重構,其實是涉及到負載因子的性能問題

<strong>loadFactor負載因子</strong>
 上面說過loadFactor是一個hashMap的決定性屬性,HashSet和HashMap的默認負載因子都是0.75,它表示,如果哈希表的容量超過3/4時,將自動成倍的增加哈希表的容量,這個值是權衡了時間和空間的成本,如果負載因子較高,雖然會減少對內存空間的需求,但也會增加查找數據的時間開銷,無論是put()和get()都涉及到對數據進行查找的動作,所以負載因子是不適宜設置過高

<img width="496" height="250" alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/jar/hashmap2.png" />

<strong>get(key)</strong>

接下來看看get(key)做了什麼

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> V get(Object key) {
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (key </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> getForNullKey();
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> hash(key.hashCode());
         </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);"> (Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table[indexFor(hash, table.length)];
              e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
              e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next) {
             Object k;
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e.hash </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">&&</span><span style="color: rgb(0, 0, 0);"> ((k </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.key) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> key </span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);"> key.equals(k)))
                 </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> e.value;
         }
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
     }</span></div>
這些動作似乎是跟put(key,value)相識,通過hash算法獲取key的hash碼,再通過indexFor定位出該key存在於table的哪一個下表,獲取該下標然後對下標中的鏈表進行遍歷比對,如果有符合就直接返回該key的value值。

<strong>keySet()</strong>

這裏還涉及另一個問題,上面說了HashMap是跟set沒有任何親屬關係,但map也一樣實現了keySet接口,下面譜析一下keySet在hashMap中是如何實現的,這裏給出部分代碼,請結合源碼查看

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 255);"> public</span><span style="color: rgb(0, 0, 0);"> K next() {
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> nextEntry().getKey();
         }

     </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> nextEntry() {
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (modCount </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> expectedModCount)
                 </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> ConcurrentModificationException();
             Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> next;
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
                 </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> NoSuchElementException();

             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> ((next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">) {
                 Entry[] t </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
                 </span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);"> (index </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> t.length </span><span style="color: rgb(0, 0, 0);">&&</span><span style="color: rgb(0, 0, 0);"> (next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> t[index</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">]) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
                     ;
             }
         current </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e;
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> e;
         }</span></div>
代碼很簡單,就是對每個格子裏面的鏈表進行遍歷,也正是這個原因,當我們依次將key值put進hashMap中,但在使用map.entrySet().iterator()進行遍歷時候卻不是put時候的順序。

<strong>擴容</strong>
 在前面說到put函數的時候,已經提過了擴容的問題

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (size</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">>=</span><span style="color: rgb(0, 0, 0);"> threshold)
 resize(</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> table.length);</span></div>

這裏一個是否擴容的判斷,當數據達到了threshold所謂的重構因子,而不是HashMap的最大容量,就進行擴容。
<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">
    </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> resize(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> newCapacity) {
         Entry[] oldTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> oldCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> oldTable.length;
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (oldCapacity </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY) {
             threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Integer.MAX_VALUE;
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">;
         }

         Entry[] newTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry[newCapacity];
         transfer(newTable);
         table </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable;
         threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(newCapacity </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> loadFactor);
     }

     </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> transfer(Entry[] newTable) {
         Entry[] src </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> newCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable.length;
         </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> j </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; j </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> src.length; j</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">) {
             Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> src[j];
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">) {
                 src[j] </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
                 </span><span style="color: rgb(0, 0, 255);">do</span><span style="color: rgb(0, 0, 0);"> {
                     Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next;
                     </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> i </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> indexFor(e.hash, newCapacity);
                     e.next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable[i];
                     newTable[i] </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e;
                     e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> next;
                 } </span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);"> (e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">);
             }
         }
     }</span></div>

 transfer方法實際上是將所有的元素重新進行一些hash,這是因爲容量變化了,每個元素相對應的hash值也會不一樣。

<strong>使用HashMap</strong>

 1.不要再高併發中使用HashMap,HashMap是線程不安全,如果被多個線程共享之後,將可能發生不可預知的問題。
2.如果數據大小事固定的,最好在初始化的時候就給HashMap一個合理的容量值,如果使用new HashMap()默認構造函數,重構因子的值是16*0.75=12,當HashMap的容量超過了12後,就會進行一系列的擴容運算,重建一個原來成倍的數組,並且對原來存在的元素進行重新的hash運算,如果你的數據是有成千上萬的,那麼你的成千上萬的數據也要跟這你的擴容不斷的hash,這將產生高額的內存和cpu的大量開銷。

 當然啦,HashMap的函數還有很多,不過都是基於table的鏈表進行操作,當然也就是hash算法,Map & hashMap在平時我們的應用非常多,最重要的是我們要對每句代碼中每塊數據結構變化心中有數。



 上面主要是參考了jdk源碼,數據結構和一些相關資料本着好記性不如爛博客的精神記錄下來,希望朋友們如果發覺哪裏不對請指出來,虛心請教
</div></div>

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