HashMap源碼粗略解讀(面試必問)

本文主要以幾個方面來講解一下HashMap:
1、HashMap默認容量
2、HashMap如何擴容
3、HashMap的數組大小爲什麼一定要是2的冪
4、HashMap爲什麼是線程不安全的
5、Java7到Java8做了哪些改進

1、HashMap的默認容量

從HashMap的構造函數說起。
HashMap1.8的構造函數

initialCapacity表示的是初始化的容量,默認是1<<4(也就是16);

loadFactor表示的是擴容因子,默認是0.75f(也就是面試常問的3/4)

爲啥擴容因子默認是0.75f?(HashMap的源碼翻譯)

HashMap1.8負載因子0.75的解釋
假如你創建HashMap的時候傳入一個不是2的冪的初始值,HashMap會把它轉換爲離它最近的2的冪的值。假設你輸入7,HashMap會默認把他轉換爲8;輸入29,會默認幫你轉換爲32

2、HashMap如何擴容?

當put進去的容量大於初始容量*擴容因子時,進行resize操作,就是把初始容量<<1(就是乘以2)進行擴容。
在這裏插入圖片描述

源碼太長,只截圖一部分。
在這裏插入圖片描述
1.7的resize操作和transfer操作在1.8合併爲resize操作。

3、HashMap的數組大小爲什麼一定要是2的冪?

首先先說明數組的大小開闢是在put操作而不是在構造函數階段,這樣爲了防止創建HashMap的時候就開闢桶的空間,導致浪費,所以在進行put操作的時候纔會開闢空間。
在這裏插入圖片描述

因爲hashcode(key)運算完將近有42億個值,需要均勻的分佈在16個桶裏面,所以採用的是與運算。

爲啥不能用取餘操作呢?

  1. 因爲hash%n的話,假設hash算出來是負數,任何負數進行%運算都是負數
  2. 因爲%運算的本質就是不停的使用除法,沒有位運算(&)來的效率高

然後就是因爲需要用到與運算,假如數組長度不是2的冪會導致與運算完的結果有一部分是0,導致HashMap的不均勻分佈。所以數組大小一定要是2的冪。爲了使HashMap均勻分佈,同時還要提高計算機的運行效率,還要把hash%數組長度改爲hash&(數組長度-1)。

4、HashMap爲什麼是線程不安全的?

HashMap的官方源碼用加粗的標籤表明了是該實現是不同步的,也就是線程不安全的。要是有大量併發還用HashMap的話,肯定由你們開發者自己背鍋。
在這裏插入圖片描述

線程不安全的註釋翻譯如下:
在這裏插入圖片描述

5、Java7到Java8做了哪些改進?

1、hash算法的計算方式不同。
在這裏插入圖片描述在這裏插入圖片描述

2、jdk1.7的擴容操作在併發場景下會發送死鎖現象,在jdk1.8就改進了。對於怎樣產生死鎖感興趣的可以去搜“codeshell hashmap”(需要翻牆然後在google上搜索)。其實就rehash的重新插入直接把按照鏈表的順序拿下來插入新的鏈表中。感興趣的可以google。

3、jdk1.7經典的數組+鏈表變成了jdk1.8的數組+鏈表+紅黑樹

鏈表的長度閾值到達8就會轉換成紅黑樹。爲啥閾值是8?官方給出的源碼翻譯如下:
在這裏插入圖片描述在這裏插入圖片描述

就是到達8的機率已經非常非常接近0了,所以認爲幾乎不可能達到9,所以閾值設置爲8。

PS:以上就是我對HashMap1.7和1.8源碼解讀所得出來的結論。假如有哪裏寫得不對的可以指出批評和修改,也可以一起探討HashMap的學習。

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