1. 程式人生 > >HashMap源碼解讀(jdk1.8)

HashMap源碼解讀(jdk1.8)

不一定 問題 tor inf jdk1.8 預算 二叉 left 朋友

  一般在工作中,只有出問題的時候才會看看源碼,很少有時間去單獨看一下源碼。

正好還沒找到工作,朋友提了一句看看HashMap,所以花了點時間看了看。

對於翻源碼這件事情,如果沒有使用過,自己會像無頭蒼蠅一樣,不知道從哪裏開始。

對於HashMap,常用作key value容器,基本的使用方式,就是new 一個實例,put、get,或者通過keyset、entry遍歷。

Map map = new HashMap();

map.put(XX,XX); map.get(XX,XX);

  map.keySet(); map.entrySet();

按照這個思路,我開始看源碼了:

1.看構造函數

可入參的變量只有兩個,中文翻譯過來,一個是容量cap,一個是因子factor。

默認cap=16,factor=0.75。其實還有一個極限值,默認情況下是16*0.75=12,在resize()裏面。

技術分享圖片

2.存儲結構

最外層是數組Node<K,V>[] table;

在put或get的時候,都會取key的hash值無符號右移做做異或預算,形成新的hash值,並用hash值作為數據下標,存入或者定位Node。

技術分享圖片

table數組中存放的不止是Node對象,還有TreeNode對象,TreeNode繼承自LinkedMap.Entry<K,V>,LinkedMap.Entry<K,V>繼承自Node。

其中,使用Node或者TreeNode是有要求的,類中有TREEIFY_THRESHOLD和UNTREEIFY_THRESHOLD作為轉化的數量參考。

Node本身是鏈表結構,當相同hash值的key被存入,會對比hash值大小,來決定Node.next的值。

TreeNode中,則是主要維護TreeNode.left和TreeNode.right.

就是說,雖然是數組,但數組中的每一個Node,不一定只存了一個key和value。

按照HashMap中hash方法的說明,已經盡量減少沖突了。

基本結構如圖

技術分享圖片

3.容量變化

根據resize()方法,當size>極限值,就是第一條中說的,默認是16*0.75的,容量和極限值都會左移1位,即乘2。

當Node裏面的數量>=TREEIFY_THRESHOLD-1的時候,該Node需要轉化為TreeNode

技術分享圖片

4.額外說明

在HashMap中,設置初始容量之後,初始容量不一定等於你設置的數量。

只是16恰巧滿足條件,計算初始容量的代碼如下

技術分享圖片

這個方法,我的結論是為了保證容量2的n次方。

減一是因為2進制從0開始,又因為int轉2進制,打印出來一共32位,所以分別右移2、4、8、16,將設定的數值的2進制lowbit都變成1,這樣最後加1後,得到的數字肯定是2的n次方。

我的猜測是,因為使用了TreeNode,所以極限情況下,就是數組中全是TreeNode,達到極限的數量時,能滿足二叉樹結構的數量。

HashMap源碼解讀(jdk1.8)