HashMap底層原始碼剖析

2021-10-04 01:31:46 字數 3032 閱讀 1458

陣列+單向鍊錶+紅黑樹

陣列:陣列每一項都是乙個鍊錶,其實就是陣列和鍊錶的結合體

單向鍊錶:當法神hash碰撞時,首先會找到陣列對應位置,然後1.8採用尾插入法(1.7採用頭插入法),形成乙個單項鍊表結構

紅黑樹:當陣列中每項的鍊錶長度大於8時,會轉換為紅黑樹

hash碰撞:不同的key可能會產生相同的hash值;

方案:鍊錶發,再雜湊法;

hashmap中採用鍊錶發,在concurrenthashmap中採用雜湊法;

二叉查詢樹在特殊情況下也會變成線性結構,和原來鍊錶有共同的問題,節點太深,查詢效能慢;

紅黑樹相比二叉樹,在檢索的時候效率其實差不多,都是通過平衡來二分查詢。但對於插入刪除等操效率提高很多。紅黑樹不像二叉樹一樣追求絕對的平衡,它允許區域性很少的不完全平衡,這樣對於效率影響不大,但省去了很多沒有必要的調平衡操作,二叉樹調平衡有時候代價較大,所以二叉樹的效率不如紅黑樹;

使用紅黑樹主要用於提高查詢速度;

當資料較少的時候,採用鍊錶要比紅黑樹效率高,因為平衡二叉樹保持平衡需要耗費資源,那麼前期資料較少時採用鍊錶,當鍊表中的資料長度大於8時,就將鍊錶轉換成紅黑樹,可以加快資料的插敘速度,官方測試8為效能最優。

public v put(k key, v value)

計算key的hash值,然後將hash值以及key值本身和value傳遞到putval方法中;

final v putval

(int hash, k key, v value,

boolean onlyifabsent,

boolean evict)

//再次進行key的重複判斷

if(e.hash == hash &&

((k = e.key)

== key ||

(key != null && key.

equals

(k))))

break

; p = e;}}

//表明,記錄到具有相同元素的節點

if(e != null)

}++modcount;

//判斷當前陣列元素的個數和閾值進行比較,如果數量大於閾值則需要擴容if(

++size > threshold)

//預設情況下,第一次新增資料的時候,先會進行一次擴容後再新增資料,後續都是先新增資料在進行擴容

resize()

;afternodeinsertion

(evict)

;//這個是空函式,可以有使用者根據需要覆蓋

return null;

}

在上述的方法中,設計三種情況:第一種情況,陣列索引位置沒有鍵值對,處理方式就是直接把待新增鍵值對封裝成node新增到索引位置即可;第二種情況,如果陣列索引位置有鍵值對,而且封裝的treenode節點,處理方式是呼叫紅黑樹的插入方法,把帶新增鍵值對新增到紅黑樹中;第三種情況,同樣陣列索引位置有鍵值對,但是封裝的是node節點,處理方法就比較複雜,首先把待新增鍵值對封裝成node節點新增到鍊錶尾部,然後判斷當前鍊錶長度,如果達到閾值,就判斷是擴容還是轉換為紅黑樹;

進入getnode方法:

final node

getnode

(int hash, object key)

while

((e = e.next)

!= null);}

}return null;

}

在hashmap中,桶陣列的長度均是2的冪,閾值大小為桶陣列長度與負載因子的乘積。當hashmap中的鍵值對數量超過閾值時,就進行擴容;

擴容之後,要重新計算鍵值對的位置,並把它們移到合適的位置上去;

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;

@suppresswarnings()

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

node

newtab =

(node

)new

node

[newcap]

;table = newtab;

if(oldtab != null)

else

}while

((e = next)

!= null)

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

if(lotail != null)

if(hitail != null)}}

}}return newtab;

}

1.計算新桶陣列的容量newcap和新閾值newthr

2.根據計算出的newcap建立新的桶陣列,桶陣列table也是這裡進行初始化的

3.將鍵值對節點重新對映到新桶陣列中,如果節點是treenode型別,則需要拆分紅黑樹;如果是普通節點,則節點按原順序進行分組

HashMap底層原始碼解析

目錄 一 分析hashmap的資料結構 1.使用陣列儲存,加快訪問速度 2.陣列中的鍊錶,解決hash衝突 3.使用紅黑樹優化鍊錶,防止大量hash衝突 二 hashmap主要原始碼解讀 三 總結 在看原始碼之前,了解一下它的資料結構和執行過程,才能更快更加有效率的讀懂原始碼。hashmap實際儲存...

HashMap底層原始碼實現

首先需要明確的是 hashmap 內部結構 可以看作是陣列和鍊錶結合組成的復合結構,陣列被分為乙個個桶 bucket 每個桶儲存有乙個或多個entry物件,每個entry物件包含三部分 key 鍵 value 值 next 指向下乙個entry 通過雜湊值決定了entry物件在這個陣列的定址 雜湊值...

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