天天看點

HashMap源碼解讀(面試必問)

本文主要以幾個方面來講解一下HashMap:

1、HashMap預設容量

2、HashMap如何擴容

3、HashMap的數組大小為什麼一定要是2的幂

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

5、Java7到Java8做了哪些改進

1、HashMap的預設容量

從HashMap的構造函數說起。

HashMap源碼解讀(面試必問)

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

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

為啥擴容因子預設是0.75f?(HashMap的源碼翻譯)

HashMap源碼解讀(面試必問)

假如你建立HashMap的時候傳入一個不是2的幂的初始值,HashMap會把它轉換為離它最近的2的幂的值。假設你輸入7,HashMap會預設把他轉換為8;輸入29,會預設幫你轉換為32

2、HashMap如何擴容?

當put進去的容量大于初始容量*擴容因子時,進行resize操作,就是把初始容量<<1(就是乘以2)進行擴容。

HashMap源碼解讀(面試必問)

源碼太長,隻截圖一部分。

HashMap源碼解讀(面試必問)

1.7的resize操作和transfer操作在1.8合并為resize操作。

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

首先先說明數組的大小開辟是在put操作而不是在構造函數階段,這樣為了防止建立HashMap的時候就開辟桶的空間,導緻浪費,是以在進行put操作的時候才會開辟空間。

HashMap源碼解讀(面試必問)

因為hashcode(key)運算完将近有42億個值,需要均勻的分布在16個桶裡面,是以采用的是與運算。

為啥不能用取餘操作呢?

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

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

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

HashMap的官方源碼用加粗的标簽表明了是該實作是不同步的,也就是線程不安全的。要是有大量并發還用HashMap的話,肯定由你們開發者自己背鍋。

HashMap源碼解讀(面試必問)

線程不安全的注釋翻譯如下:

HashMap源碼解讀(面試必問)

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

1、hash算法的計算方式不同。

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

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

3、jdk1.7經典的數組+連結清單變成了jdk1.8的數組+連結清單+紅黑樹

連結清單的長度門檻值到達8就會轉換成紅黑樹。為啥門檻值是8?官方給出的源碼翻譯如下:

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

就是到達8的幾率已經非常非常接近0了,是以認為幾乎不可能達到9,是以門檻值設定為8。

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