HashMap 原始碼面試

2022-04-04 16:28:37 字數 4288 閱讀 7710

問:

先講講hashmap 的資料結構

jdk8 中 hashmap 是 陣列 + 鍊錶 + 紅黑樹, 每個資料單元是乙個 node 結構。(jdk 1.7 是 entry 陣列,資料單元是 entry)

node 結構有 key 字段, 有 value 字段, 還有 next 字段,還有 hash 字段。

next 字段,就是發生 hash 衝突的時候,當前桶位中的 node 於衝突 node 

問:

hashmap 預設長度是 多少

答:

16

問:

雜湊表 是初始化的時候建立的,還是什麼時候建立的。

答:

雜湊表是懶載入機制,只有第一次 put 資料的時候,才建立。

問:

預設的負載因子是多少, 有啥作用

0.75.

計算擴容閾值用的,    

問:

鍊錶轉化為紅黑樹需要什麼條件

答:

鍊錶長度超過 8 的時候 和 當前雜湊表長度已經達到 64.。

否則,slot 內煉表長度到8, 也不會轉樹,僅僅發生一次 resize, 雜湊表擴容。

問:

node 物件內部,有乙個 hash 字段, 然後這個 hash 欄位的值 是 key 物件的 hashcoded 返回值嗎。

答:

這個不是的。

高低位異或後 增大定址的隨機性,減少 hash 衝突

hashmap put  寫資料的具體流程。

答:

put 資料的時候,先根據 key # hashcode 經過高低位異或,再按 位於&(table.lenth - 1)得到槽位下表,

根據這個槽內的情況,put 的方式也不同

有四種情況,1、槽位為空 slot == null,2、槽位不為空,但是 node 還沒有鍊錶化; 3、slot 內的node 已經鏈化,4、已經是slot內是紅黑樹。

1、槽位為空 slot == null;

直接占用。根據 key 和 value 直接包裝成 乙個 node 物件放入 slot.

2、槽位不為空,但是 node 還沒有鍊錶化

先對比 node 的key  和 put 物件的 key 是否完全相等;

如果相等,再equal 相等,把新的 key value 替換當前 slot 的key value.

如果不相等,就是 hash 衝突,(jdk8)是是哦那個尾插法, 在原有的slot 裡的資料物件使用 next 插入新的物件。

3、slot 內的node 已經鏈化,

先對比 鍊錶上的節點,如果有一致的話,還是替換操作。

如果一樣的節點,則追加到鍊錶的尾部。(在 jdk7, 中如果插到鍊錶頭部,則可能形成迴圈鍊錶,尾插法則不會。)

並檢查是否達到閾值。

4、已經是slot內是紅黑樹。

treenode 繼承了 node 結構,在 node 基礎上加了個字段, 分別是指向父節點的 parent,  指向左子節點left,指向右子節點的right,

還有表示節點的 red. 

然後,紅黑樹的插入操作,首先是找到乙個合適的插入點,即插入節點的父節點,然後這個紅黑樹滿足二叉樹所有的排序特性,

找父節點的操作和二叉樹(排序)樹完全一致的,這個二分查詢演算法對映出來的結構,就乙個倒立的二叉樹,左節點小於當前節點,右節點

大於當前節點,每次向下查詢一層,就排除掉一般資料。

查詢也分情況。第一種情況是一直向下查詢,直到左子樹或者右子樹為 null, 說明整個樹中, node 的 key 與當前 put key 一致的

treenode. 此時,就是探測節點就是父節點所在,當前插入節點,插入到父節點的左子樹或者是右子樹。根據,這個插入節點的 hash 值和

父節點 hash 值大小決定 左右,插入會打破平衡,還需要乙個紅黑樹的平衡演算法。

(還有左旋,右旋)

第二種,情況就是,根節點向下探測的過程種,發現這個 treenode 的 key 與當前 put 的 key 完全一致,就直接替換這個相同的節點。

(這裡有點蒙混,替換了還要看是否滿足二叉樹查詢樹的特點。)

問:

紅黑樹的幾個原則。

答:

1、構成樹的節點,有乙個顏色屬性,紅 和 黑。

2、根節點,必須是黑色的。 

3、從葉節點,到根節點的路徑上,每條路勁黑色節點一定是一致的。 

3、紅色節點不能相連,就是紅色節點子節點一定為黑色。

問:

左旋 和 右旋 是是什麼

問:

為什麼要引入,紅黑樹這個結構。

答:解決 hash 衝突導致的鏈化嚴重的問題。

鍊錶查詢慢,通過紅黑樹,提高查詢節點的速度,就是以空間換時間。

本身雜湊表理想查詢效率 為 o (1), 但是鏈化比較導致查詢速度為 o(n)  ,引入紅黑樹,查詢效率為 o(lgn)

問:

為什麼鏈化 導致查詢速度低。

答:

如果 查詢目標在 鍊錶尾部,要乙個乙個 next 去查詢。

問:

擴容機制,什麼時候會觸發這個擴容

答:

擴容的規則

問:

為什麼採用位移運算, 而不乘以 2

這裡考慮到效能運算,cpu 不支援乘法運算,所有乘法運算,在指令層面轉化為 加法實現。 

如果,用位運算 對 cpu 就簡潔高效,效率比較高。

問:

如何進行資料遷移

分四種情況,第一種情況,slot 儲存的是 null, 第二種情況;儲存的是個 node, node 沒有鏈化;第三章,slot 儲存乙個鏈化的node,

第四種,儲存乙個紅黑樹的根節點 treenode 物件。

第一種情況,slot 儲存的是 null。不用說,

第二種情況;儲存的是個 node, node 沒有鏈化, 直接遷移就好了,根據新錶的 tablesize 計算出 新錶的位置,然後存放過去就可以了。

第三種情況:需要把當前 slot 儲存的 鍊錶,拆分成兩個鍊錶,分別是 高位鏈 和 低位鏈,所有的 node  hash欄位轉化為二進位制後,低位

都是相同的,低位就是老表的 tablesize - 1 轉化出來的 二進位制數有效位。

table 陣列 長度 16, 16 -1 就是 15, 然後 15 轉化出來的二級制數就是 1111.  低四位,高位是第5位,01111 .。。。。(這個老哥不太行)

問:

紅黑樹怎麼擴容

答:

紅黑樹節點物件,這個 treenode 結構啊, 依然保留了 next 字段, 紅黑樹結構內部還維護著乙個鍊錶,

新增和刪除節點,仍然要維護這個鍊錶,這個鍊錶方便 拆分這個紅黑樹用的。也就是根據這個 高位 和 低位, 拆為 高位鏈 和 低位鏈。

高位鏈資料,要存放到 新錶, 下標就是 老表的位置 + 老表的陣列長度,然後計算出來的這個位置。

低位鏈存放的位置和老表位置一樣。

如果 鍊錶長度小於 8, 直接把這個 treenode 轉化為普通的 node 鍊錶, 然後放在擴容後的新錶就可以了。

如果拆分出來的鍊錶,長度大於 8,還是需要把這個鍊錶公升級為紅黑樹。

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...