1. 程式人生 > >HashMap存儲原理

HashMap存儲原理

創建 包含 這樣的 不同的 可能 現象 兩個 相同 與運算

1. HashMap概述

HashMap是基於哈希表的Map接口的非同步實現。此實現提供所有可選的映射操作,並允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恒久不變。

2. HashMap的數據結構

在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。

3. HashMap的存取
HashMap的功能是通過“鍵(key)”能夠快速的找到“值”。下面我們分析下HashMap存數據的基本流程:
1、 當調用put(key,value)時,首先獲取key的hashcode,int hash = key.hashCode();
2、 再把hash通過一下運算得到一個int h.
hash ^= (hash >>> 20) ^ (hash >>> 12);
int h = hash ^ (hash >>> 7) ^ (hash >>> 4);
為什麽要經過這樣的運算呢?這就是HashMap的高明之處。先看個例子,一個十進制數32768(二進制1000 0000 0000 0000),經過上述公式運算之後的結果是35080(二進制1000 1001 0000 1000)。看出來了嗎?或許這樣還看不出什麽,再舉個數字61440(二進制1111 0000 0000 0000),運算結果是65263(二進制1111 1110 1110 1111),現在應該很明顯了,它的目的是讓“1”變的均勻一點,散列的本意就是要盡量均勻分布。那這樣有什麽意義呢?看第3步。
3、 得到h之後,把h與HashMap的承載量(HashMap的默認承載量length是16,可以自動變長。在構造HashMap的時候也可以指定一個長 度。這個承載量就是上圖所描述的數組的長度。)進行邏輯與運算,即 h & (length-1),這樣得到的結果就是一個比length小的正數,我們把這個值叫做index。其實這個index就是索引將要插入的值在數組中的 位置。第2步那個算法的意義就是希望能夠得出均勻的index,這是HashTable的改進,HashTable中的算法只是把key的 hashcode與length相除取余,即hash % length,這樣有可能會造成index分布不均勻。還有一點需要說明,HashMap的鍵可以為null,它的值是放在數組的第一個位置。
4、 我們用table[index]表示已經找到的元素需要存儲的位置。先判斷該位置上有沒有元素(這個元素是HashMap內部定義的一個類Entity, 基本結構它包含三個類,key,value和指向下一個Entity的next),沒有的話就創建一個Entity<K,V>對象,在 table[index]位置上插入,這樣插入結束;如果有的話,通過鏈表的遍歷方式去逐個遍歷,看看有沒有已經存在的key,有的話用新的value替 換老的value;如果沒有,則在table[index]插入該Entity,把原來在table[index]位置上的Entity賦值給新的 Entity的next,這樣插入結束。
總結:keyàhashcodeàhàindexà遍歷鏈表à插入

4. 擴展問題

要同時復寫equals方法和hashCode方法。

按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同。

如果兩個不同對象的hashCode相同,這種現象稱為沖突,沖突會導致操作哈希表的時間開銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。

如果相同的對象有不同的hashCode,對哈希表的操作會出現意想不到的結果(期待的get方法返回null),

再回頭看看前面提到的為什麽覆蓋了equals方法之後一定要覆蓋hashCode方法,很簡單,比如,String a = new String(“abc”);String b = new String(“abc”);如果不覆蓋hashCode的話,那麽a和b的hashCode就會不同,把這兩個類當做key存到HashMap中的話就 會出現問題,就會和key的唯一性相矛盾。

HashMap存儲原理