【Java】說HashMap鏈表長度超過8就會轉換成紅黑樹的出來捱打!

  • 先上結論:在極限情況下,某一個位置鏈表長度達到11時,纔會轉換成紅黑二叉樹結構。
  • 這個極限情況指的是每次都是因爲某個位置鏈表長度而導致的數組擴容,比如說數據只向數組中一個位置添加數據。
  • 網上很多人說jdk1.8後HashMap鏈表長度超過8就會轉換成紅黑樹,通過源碼閱讀和測試後已經可以確定並不是超過8個就變紅黑樹,鏈表長度超過8,只是會嘗試轉換爲二叉樹,轉換前,首先判斷數組容量是否足夠大,如果不夠大,則進行擴容,發現長度小(未達到64),則擴容爲原來的2倍,如果達到64,假設數組某個位置鏈表長度已經爲10,如果這個位置還有entry進來即爲11個時,才變成紅黑樹!具體驗證過程如下:
驗證方式:通過debug的方式查看實際的過程。
具體的過程如下:
   	1.前提,向hashmap中添加數據,只讓數據只向集合數組中的同一個位置進行添加。
   		如何保證數據只向數組中同一個索引位置添加數據呢?
   	 	a、對key進行hashcode,然後再和(length-1)進行 & 運算,結果相同的數據進入數組中同一位置;
   	 	b、如果key的hashcode不同只是分配位置相同,則直接添加進鏈表,如果hashcode相同則通過equals()方法進行比較以分辨key是否真的重複(hashcode相同key不一定同)
   	 	c、hashcode值相同通過調用key中equals()方法比較新增數據和原數組位置的鏈表數據key是否重複,不重複則添加成功,重複則替換;
   	 	所以我們的測試程序只要保證每個數據key的hashcode相同,equals()方法的返回值保持不同即可。
   	2.具體過程源碼分析及現象
   		a、當hashmap某一個位置,鏈表數據的個數超過8個(即當有9個時),會嘗試轉換爲二叉樹;
   		b、轉換前,首先判斷數組容量是否足夠大,如果不夠大,則進行擴容,發現16長度小,則擴容爲原來的2倍,數組的長度16*2=32;
   		c、添加第10個數據時,同樣嘗試轉換爲二叉樹,轉換前,發現32長度還較少,則繼續擴容,此時數組的長度爲32*2=64;
   		d、添加第11個數據時,同樣嘗試轉換爲二叉樹,轉換前,發現64長度已經不少了,則將鏈表轉換爲紅黑樹。
  • 其他相關問題
    問題:通過源碼閱讀後發現HashMap底層和很多框架一樣也是用相與的方式進行數據位置的分配,此時爲什麼要用2的整數倍進行擴容呢?
    答:之所以是2的整數倍進行擴容是因爲底層用的與運算而不是取餘,爲了在與hash進行與運算的時候,使每個位置都有機會被分配,如果不是2的整數倍與運算時便會有位置爲固定爲0,結果不可能是1,導致擴容後有位置沒有數據的進入,我們以“book”的Key來演示整個過程,具體分析過程如下
與運算公式如下(Length是HashMap的長度):

	index = HashCode(Key) & (Length - 1)

	1.計算book的hashcode,結果爲十進制的3029737,二進制的1011100011101011101001。
	
	2.假定HashMap長度是默認的16,計算Length-1的結果爲十進制的15,二進制的1111。
	
	3.把以上兩個結果做與運算,101110001110101110 1001 & 1111 = 1001,十進制是9,所以 index=9。

	可以說,Hash算法最終得到的index結果,完全取決於Key的Hashcode值的最後幾位。
	
	假設HashMap的長度是10,重複剛纔的運算步驟: 

在這裏插入圖片描述

	單獨看這個結果,表面上並沒有問題。
	我們再來嘗試一個新的HashCode 101110001110101110 1011

在這裏插入圖片描述

  • 雖然HashCode的倒數第二第三位從0變成了1,但是運算的結果都是1001。也就是說,當HashMap長度爲10的時候,有些index結果的出現機率會更大,而有些index結果永遠不會出現,比如1111,那麼這個位置擴容後將一直沒有數據進入。這樣顯然不符合Hash算法均勻分佈的原則。反觀長度16或者其他2的冪,Length-1的值是所有二進制位全爲1,這種情況下,index的結果等同於HashCode後幾位的值。只要輸入的HashCode本身分佈均勻,Hash算法的結果就是均勻的。
    在這裏插入圖片描述

用博客見證成長,用行動證明我在努力。
如果你有緣看到我博客,對你有幫助、喜歡博客內容,請“點贊” “評論”“收藏”一鍵三連哦!

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