聽說你看過HashMap原始碼,來面試下這幾個問題

2021-10-01 18:56:27 字數 2801 閱讀 7570

hashmap原始碼解析系列文章

jdk8 hashmap原始碼行級解析 史上最全最詳細解析

jdk8 hashmap原始碼行級解析 紅黑樹操作 史上最全最詳細**

jdk8 hashmap原始碼 putmapentries解析

jdk8 hashmap原始碼 clone解析

深入理解hashmap:那些巧妙的位操作

聽說你看過hashmap原始碼,來面試下這幾個問題

entry table。這個entry型別的陣列儲存了hashmap的真正資料。

size大小。代表hashmap內儲存了多少個對映。

capacity容量。實際上hashmap沒有乙個成員叫capacity,它是作為table這個陣列的大小而隱式存在。

threshold閾值和loadfactor裝載因子。threshold是通過capacity * loadfactor得到的。當size超過threshold時(剛好相等時不會擴容),hashmap會擴容再雜湊。

entrysetkeysetvalues這三個都是一種檢視,真正的資料都來自table

這是為了能夠通過&位操作來得到乙個對映的所在table陣列下標。

正常來說,通過key的hash值 % table.length可以得到key所應該在的陣列下標,但如果這個容量是2的冪,那麼 陣列下標可以通過key的hash值 & (table.length-1)得到。位操作肯定比取餘操作快多了。

通過hash實現了對對映的快速訪問。key可以有乙個null,value可以重複。

非同步,是執行緒不安全的。單執行緒下,使用hashmap相比使用hashtable效率更高。

底層是hash表,所以不保證順序。因為每次resize都會重新分配元素到各個雜湊桶。

首先判斷table成員是否初始化,如果沒有,則呼叫resize。

通過傳入鍵值對的key的hashcode和容量,馬上得到了該對映所在的table陣列下標。並通過陣列的取下標操作,得到該雜湊桶的頭節點。

如果沒有發生雜湊碰撞(頭節點為null),那麼直接執行新增操作

如果發生了雜湊碰撞(頭節點不為null),那麼分為兩種情況:

如果與桶內某個元素==返回true,或者equals判斷相同,執行替換操作

如果與桶內所有元素判斷都不相等,執行新增操作

新增操作做完後會有兩個判斷:

如果雜湊桶是單鏈表結構,且桶內節點數量超過了treeify_threshold(8),且size大於等於了min_treeify_capacity(64),那麼將該雜湊桶轉換為紅黑樹結構。

如果新增後size大於了threshold,那麼呼叫resize。

當table還沒初始化時,使用預設容量16或者通過使用者給定容量算出乙個2的冪,來作為table的大小。

當table已經初始化了,那麼擴容成舊容量的2倍。如果新容量為最大容量,則將閾值設為int最大值。

當table已經初始化,且舊容量已經是最大容量,那麼table不再進行擴容。

雜湊桶內的各個元素,經過擴容後,它的可能的table下標要麼在原table下標,要麼在原table下標 + 舊容量。正是因為容量永遠為2的冪,才使得擴容操作如此簡單。

通過傳入對映的key的hashcode和容量,馬上得到了該對映所在的table陣列下標。並通過陣列的取下標操作,得到該雜湊桶的頭節點。

如果頭節點為null,那麼代表沒有該對映。

如果頭節點不為null,且傳入對映與桶內某個對映==返回true,或者equals判斷相同,那麼代表找到了該對映。

如果頭節點不為null,且傳入對映與桶內每個對映都判定不相等,那麼代表沒有該對映。

key的hashcode與該hashcode的無符號右移16位,異或起來得到的。

因為當table的size比較小時,能影響到table下標的,只有雜湊值幾個低位bit,這很可能會加劇雜湊碰撞。但這樣實現後,雜湊值的高16位bit保持不變,低16位則受到高16位的「擾動」而發生改變,這樣就使得高位bit也能影響table下標,減少雜湊碰撞。

key的hashcode只要有乙個bit發生變化,hash函式的返回值也會跟著變化,用以減少雜湊碰撞。

如果key重寫了hashcode方法,那麼也應該重寫equals方法。

如果key只重寫了hashcode方法,卻沒有重寫equals方法。那麼會造成map裡會存在重複的我們認為「相同」的鍵值對在裡面(一般是指,兩個物件的成員是相同的)。因為如果新增了相同元素,根據put過程則發生雜湊碰撞,本來這個相同元素不應該新增,但由於原始object的equals方法邏輯使用==判斷,所以只要位址值不同就肯定能新增進去。

如果key只重寫了equals方法,卻沒有重寫hashcode方法。那麼也會造成map裡會存在重複的我們認為「相同」的鍵值對在裡面。因為使用了原始的hashcode了,有些該發生的雜湊碰撞也就不會發生了,都不在乙個雜湊桶了,即使我們認為是「相同」的,也不會去呼叫你重寫的equals方法的。

本文基於jdk1.8的hashmap原始碼。

HashMap原始碼系列 HashMap的屬性

public class hashmap extends abstractmap implements map,cloneable,serializable容載因子 容載因子越大,table陣列中儲存的資料越密集,碰撞的可能性就越大。容載因子越小,儲存越稀疏,碰撞的可能性就越小,不過浪費儲存空間。轉...

HashMap原始碼解讀

一 建立乙個hashmap都做了哪些工作?mapmap new hashmap hahmap無參構造方法 public hashmap 可以看到設定了載入因子 預設0.75 閾值 預設容量16 預設載入因子0.75 12 table是hashmap內部資料儲存結構entry陣列。當hashmap的s...

HashMap原始碼分析

public hashmap int initialcapacity,float loadfactor 2 接下來是重要的put方法,put方法用於將鍵值對儲存到map中,讓我們來具體分析一下。public v put k key,v value if key null 若key為null,則將va...