HashMap 原始碼賞析

2021-10-04 16:43:01 字數 3486 閱讀 5737

作為工作中最重要、最常用的容器之一,當然還是要自己動手寫一篇 hashmap 的原始碼解析來加深對其的印象咯,而且它的設計與實現 也有很多值得學習的地方。

jdk1.8 的hashmap 底層使用的是 動態陣列,陣列中元素存放的是 鍊錶或紅黑樹。核心原始碼如下。

public

class

hashmap

extends

abstractmap

implements

map, cloneable, serializable

public

hashmap

(int initialcapacity)

public

hashmap()

public

hashmap

(map<

?extendsk,

?extends

v> m)

public v put

(k key, v value)

final v putval

(int hash, k key, v value,

boolean onlyifabsent,

boolean evict)

// 條件為 true,表示當前鍊錶包含要插入的鍵值對,終止遍歷

if(e.hash == hash &&

((k = e.key)

== key ||

(key != null && key.

equals

(k))))

break

; p = e;}}

// 判斷要插入的鍵值對是否存在 hashmap 中

if(e != null)

}++modcount;

// 鍵值對數量超過閾值時,則進行擴容if(

++size > threshold)

resize()

;afternodeinsertion

(evict)

;return null;

}/**

* 擴容為原容量的兩倍,並將存在的元素 放到新的陣列上

*/final node

resize()

// 按舊容量和閾值的2倍計算新容量和閾值的大小

elseif(

(newcap = oldcap <<1)

< maximum_capacity &&

oldcap >= default_initial_capacity)

newthr = oldthr <<1;

// double threshold

}else

if(oldthr >0)

// initial capacity was placed in threshold

// 初始化時,將 threshold 的值賦值給 newcap,

// hashmap 使用 threshold 變數暫時儲存 initialcapacity 引數的值

newcap = oldthr;

else

// newthr 為 0 時,按閾值計算公式進行計算

if(newthr ==0)

threshold = newthr;

// 建立新的桶陣列,桶陣列的初始化也是在這裡完成的

node

newtab =

(node

)new

node

[newcap]

; table = newtab;

if(oldtab != null)

else

}while

((e = next)

!= null)

;// 將分組後的鍊錶對映到新桶中

if(lotail != null)

if(hitail != null)}}

}}return newtab;

}public v get

(object key)

/** * 根據 hash 和 key 獲取相應的 node節點

*/final node

getnode

(int hash, object key)

while

((e = e.next)

!= null);}

}return null;

}/**

* 還記hashmap底層的動態陣列的定義嗎 transient node table

* 這裡很明顯是乙個單向鍊錶結構

*/static

class

node

implements

map.entry

public

final k getkey()

public

final v getvalue()

public

final string tostring()

public

final

inthashcode()

public

final v setvalue

(v newvalue)

public

final

boolean

equals

(object o)

return

false;}

}/**

* jdk8 加入的 紅黑樹treenode內部類,紅黑樹的方法比較複雜,這裡只展示一些重要的

* 屬性結構**

*/static

final

class

treenode

extends

linkedhashmap.entry

}}

原始碼部分 結合注釋還是很容易看懂的,比較複雜的是紅黑樹這種資料結構,以及紅黑樹與鍊錶之間的相互轉換。下面我們重點看一下這個資料結構。

紅黑樹是一種自平衡的二叉查詢樹,比普通的二叉查詢樹效率更高,它可在 o(logn) 時間內完成查詢、增加、刪除等操作。

普通的二叉查詢樹在極端情況下可退化成煉表,導致 增、刪、查 效率低下。紅黑樹通過定義一些性質,將任意節點的左右子樹高度差控制在規定範圍內,以達到平衡狀態,紅黑樹的性質定義如下。

節點是紅色或黑色。

根是黑色。

所有葉子都是黑色(葉子是nil節點)。

每個紅色節點必須有兩個黑色的子節點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點。)

從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

紅黑樹的操作和其他樹一樣,包括查詢、插入、刪除等,其查詢過程和二叉查詢樹一樣簡單,但插入和刪除操作要複雜的多,這也是其 為保持平衡性 不會退化成煉表 所付出的代價。紅黑樹為保持平衡性 所進行的操作主要有 旋轉(左旋、右旋)和變色。

紅黑樹的實現 確實比較複雜,光是理解其 插入、刪除 的操作原理 就蠻費勁,所以這裡先挖個坑,後面單獨用一篇博文來分析 hashmap的 內部類treenode 對紅黑樹資料結構的實現。

ThreadLocal 原始碼賞析

前面我們分析了 thread類的原始碼,有了前面的鋪墊,通過原始碼 理解threadlocal的秘密就容易多了。threadlocal類 提供了 get set執行緒區域性變數的實現,threadlocal成員變數與正常的成員變數不同,每個執行緒都可以通過 threadlocal成員變數 get s...

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