HashMap 底層分析

2021-10-03 03:37:12 字數 1648 閱讀 5599

以下基於 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)來找到對應的元素。

//方式1

iterator> entryiterator = map.entryset().iterator();

while (entryiterator.hasnext())

//方式2

iteratoriterator = map.keyset().iterator();

while (iterator.hasnext())

//方式3

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)

大大提高了查詢效率。

多執行緒場景下推薦使用 concurrenthashmap。

HashMap 底層分析

更多 hashmap 與 concurrenthashmap 相關請檢視這裡。以下基於 jdk1.7 分析。如圖所示,hashmap 底層是基於陣列和鍊錶實現的。其中有兩個重要的引數 容量的預設大小是 16,負載因子是 0.75,當hashmap的size 16 0.75時就會發生擴容 容量和負載因...

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