HashMap只有容量達到閥值才發生擴容嗎?

2021-08-28 00:09:00 字數 1947 閱讀 6012

看了網上很多文章,說hashmap在元素達到負載因子對應數的時候就發生擴容。如果你看過原始碼就會發現,其實還有一種情況也可能會發生擴容:樹形化的時候。

物件最終是如何放入hashmap中的?

hashmap底層是由陣列+鍊錶組成的,為了方便不懂的人更容易理解,那我們就先假設hashmap底層就是陣列,先不管鍊錶。 

當乙個物件add到hashmap中,此時hashmap的add方法是如何來確定這個物件是放在陣列中的哪個位置的呢?

拿jdk1.8來說(其他jdk版本稍有不同,但大同小異),大家應該知道每乙個物件天生都繼承了或程式設計師自己覆蓋了object類的 hashcode()方法,此方法返回物件的hashcode值。 

hashmap會有乙個方法,先拿到要add進hashmap中的物件的hashcode,再將這個hashcode異或上物件自身hashcode右移16位(是不是感覺說的不是人話?這個步驟叫擾亂,這樣做的目的是為了讓hashcode每一位都盡可能用到,如果不理解沒關係並不影響接下來的閱讀),hashcode經過上述步驟之後再&(陣列長度-1),計算的結果就是這個物件在陣列中的位置了。我自己都覺得說的不是人話,下面舉個例子,便於理解:

這裡有乙個student物件的hashcode是:a 

先把這個a右移16位 , b=a>>>16; 

然後a=a&b; 

陣列中的位置等於: a&(陣列長度-1); 

上述原始碼如下:

h=key.hashcode();

h = key.hashcode()) ^ (h >>> 16)

陣列位置=h&(陣列長度-1);

好了, 我們已經知道元素是如何在hashmap中的陣列上如何定位了,現在假設乙個極端情況(不可能發生,但是我用這個舉例子): 

假設陣列長度為1,根據原始碼: 

陣列位置=h&(陣列長度-1) 

那麼有: 

陣列位置=h&(1-1)=0 ,無論什麼物件,都定位到陣列的第0個位置。 

這個很好理解吧。無論元素是否一樣,由於陣列長度為1,所以元素通通定位到陣列中第0個位置。大家都知道乙個陣列只能放乙個元素啊?那怎麼辦呢?我們用鍊錶來解決這個問題,把定位到這個位置的元素通過鍊錶連線。這就是我一開始說的:hashmap是陣列+鍊錶。

那樹形化又是什麼東東呢? 

想一下我們為什麼要用hashmap,是因為通過hash演算法在理想情況下時間複雜度o(1)就能找到元素,特別快,但是我都說了是理想情況,如果遇到上述發生hash碰撞(誰jb取的名字,就是上面我才說的,兩個元素定位到陣列中同乙個位置),且hash碰撞比較頻繁的話,那麼當我們get乙個元素的時候,定位到了這個陣列,還需要在陣列中遍歷一次鍊錶最終才能找到要get的元素,是不是已經失去一部分使用hashmap的初心了?(因為需要遍歷鍊錶,所以時間複雜度就比之前高了) 

所以jdk1.8使用紅黑樹這種資料結構來解決鍊錶過長的問題(可以簡單理解為用紅黑樹遍歷比鍊錶遍歷速度快,時間複雜度低,不懂紅黑樹的可以去搜搜看),預設鍊錶長度達到8就將鍊錶樹形化(變為紅黑樹)。

回到最最開始我提到的,那為什麼樹形化的時候可能會發生擴容呢? 

想想剛剛的例子陣列長度為1,所有元素全部在陣列的第0個位置形成一條鍊錶,這例子是一種極端情況,陣列長度過小,那自然就會經常發生hash碰撞,那形成長鍊表是肯定的,這個時候樹形化其實是治標不治本,因為引起鍊錶過長的根本原因是陣列過短,所以在jdk1.8原始碼中,執行樹形化之前,會先檢查陣列長度,如果長度小於64,則對陣列進行擴容,而不是進行樹形化。

所以發生擴容的時候有兩種情況,一種是元素達到閥值了,一種是hashmap準備樹形化但又發現陣列太短,這兩種情況均可能發生擴容。

hashmap的容量與擴容

有幾個重要的常量: static final int default_initial_capacity = 1 4;//預設的桶陣列大小 static final int maximum_cap...

HashMap容量和負載因子

引自 hashmap底層資料結構是陣列 鍊錶,jdk1.8中還引入了紅黑樹,當鍊表長度超過8個時,會將鍊錶轉成紅黑樹,以提公升其查詢效能。那麼,給出乙個節點,hashmap是如何確定這個節點應該放在具體哪個位置呢?以jdk1.8為例 final v putval int hash,k key,v v...

HashMap初始容量為什麼是16

這裡不講 只講思路。要稍微懂點二進位制。hashmap的結構是陣列加鍊表,容量指的是陣列長度。元素放入陣列的哪個位置?這由算出來的hashcode 跟 1111 作與運算 得出陣列下標。hashcode具體演算法我也不知道 為什麼是1111,因為0000 1111共16個數,跟陣列下標 容量都一致。...

HashMap 容量為2次冪的原因

我們都知道 hashmap 的底層是乙個陣列加鍊表的結構,當向其中新增乙個元素的時候,需要根據key的hash值,去確定其在陣列中的具體位置。看原始碼,我們可以發現,確定陣列位置的實現是i n 1 hash,其中 n 代表陣列的長度,即map的容量。當n為2的冪次方時,n 1 hash 的值是均勻分...