HashMap 底層分析

2021-09-20 00:01:26 字數 1812 閱讀 9743

更多 hashmap 與 concurrenthashmap 相關請檢視這裡。

以下基於 jdk1.7 分析。

如圖所示,hashmap 底層是基於陣列和鍊錶實現的。其中有兩個重要的引數:

容量的預設大小是 16,負載因子是 0.75,當hashmapsize > 16*0.75時就會發生擴容(容量和負載因子都可以自由調整)。

首先會將傳入的 key 做hash運算計算出 hashcode,然後根據陣列長度取模計算出在陣列中的 index 下標。

由於在計算中位運算比取模運算效率高的多,所以 hashmap 規定陣列的長度為2^n。這樣用2^n - 1做位運算與取模效果一致,並且效率還要高出許多。

由於陣列的長度有限,所以難免會出現不同的 key 通過運算得到的 index 相同,這種情況可以利用鍊錶來解決,hashmap 會在table[index]處形成鍊錶,採用頭插法將資料插入到鍊錶中。

get 和 put 類似,也是將傳入的 key 計算出 index ,如果該位置上是乙個鍊錶就需要遍歷整個鍊錶,通過key.equals(k)來找到對應的元素。

iterator

> entryiterator = map.

entryset()

.iterator()

;while

(entryiterator.

hasnext()

)

iterator

iterator = map.

keyset()

.iterator()

;while

(iterator.

hasnext()

)

map.

foreach

((key,value)

->

);

強烈建議使用第一種 entryset 進行遍歷。

第一種可以把 key value 同時取出,第二種還得需要通過 key 取一次 value,效率較低, 第三種需要jdk1.8以上,通過外層遍歷 table,內層遍歷鍊錶或紅黑樹。

在併發環境下使用hashmap容易出現死迴圈。

併發場景發生擴容,呼叫resize()方法裡的rehash()時,容易出現環形鍊錶。這樣當獲取乙個不存在的key時,計算出的index正好是環形鍊錶的下標時就會出現死迴圈。

所以 hashmap 只能在單執行緒中使用,並且盡量的預設容量,盡可能的減少擴容。

jdk1.8中對hashmap進行了優化:

hash碰撞之後寫入鍊錶的長度超過了閾值(預設為8)並且table的長度不小於64(否則擴容一次)時,鍊錶將會轉換為紅黑樹

假設hash衝突非常嚴重,乙個陣列後面接了很長的鍊錶,此時重新的時間複雜度就是o(n)

如果是紅黑樹,時間複雜度就是o(logn)

大大提高了查詢效率。

github

HashMap 底層分析

以下基於 jdk1.7 分析。hashmap 底層是基於陣列和鍊錶實現的。其中有兩個重要的引數 容量的預設大小是 16,負載因子是 0.75,當hashmap的size 16 0.75時就會發生擴容 容量和負載因子都可以自由調整 首先會將傳入的 key 做hash運算計算出 hashcode,然後根...

HashMap底層實現分析

1.1 建立 mappersonmap new hashmap 在堆記憶體開闢空間。成員變數transient node table,transient代表不會被序列化,預設為null。static class node implements map.entry public final k get...

HashMap底層原始碼分析

static final int default initial capacity 1 4 aka 16表示1向左移4位,2的4次方 static final int maximum capacity 1 30 hashmap陣列的最大容量 static final float default lo...