HashMap底層實現分析

2021-10-04 08:17:30 字數 3880 閱讀 3441

1.1 建立:

mappersonmap = new hashmap<>();在堆記憶體開闢空間。

成員變數transient node table,transient代表不會被序列化,預設為null。

static

class

node

implements

map.entry

public

final k getkey()

public

final v getvalue()

public

final string tostring()

// 重寫hashcode()方法

public

final

inthashcode()

public

final v setvalue

(v newvalue)

// 重寫 equals() 方法

public

final

boolean

equals

(object o)

return

false;}

}

1.2 插入鍵值對

put方法原始碼:

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;

}

其中:put()方法呼叫了putval()方法,hash(key)是key運用hash演算法的返回值。

public v put

(k key, v value)

static

final

inthash

(object key)

為什麼通過位運算重新計算 hash?

計算桶位置時,用(n-1) & hash,因為hashmap 中桶陣列的大小 length 總是2的冪,此時,(n - 1) & hash 等價於對 length 取餘(位置不能超過桶長度),舉例:hash = 185,n = 16,餘數 = 9,這裡的hash是由鍵的 hashcode 產生:

當放入第乙個元素時,會觸發resize方法,初始化桶陣列容量。即putval()中:

if

((tab = table)

== null ||

(n = tab.length)==0

) n =

(tab =

resize()

).length;

當桶陣列容量未初始化時,resize()方法中會執行newcap = default_initial_capacity;static final int default_initial_capacity = 1 << 4;即預設初始化為16大小的node陣列。

然後執行下面的**:

if

((p = tab[i =

(n -1)

& hash]

)== null)

tab[i]

=newnode

(hash, key, value, null)

;//可轉化為

i =(n -1)

& hash;

//hash是傳過來的,其中n是底層陣列的長度,用&運算子計算出i的值

p = tab[i]

;//用計算出來的i的值作為下標從陣列取中元素

if(p == null)

hash值是hash(key),其中n是陣列的長度,目前陣列長度為16,不管這個hash的值是多少,經過(n - 1) & hash計算出來的i 的值一定在n-1之間。剛好是底層陣列的合法下標,用i這個下標值去底層陣列裡去取值,如果為null,建立乙個node放到陣列下標為i的位置。

此時的記憶體狀態:

當出現hash值衝突時,如果key值不相同,並且鍊錶長度沒有超出8(樹化閾值預設是8),執行下面的**:

p.next =

newnode

(hash, key, value, null)

;

new乙個新的node物件並把當前node的next引用指向該物件,也就是說原來該位置上只有乙個元素物件,現在轉成了單向鍊錶。

如果鍊錶長度超過樹化閾值(static final int treeify_threshold = 8;)時,將鍊錶轉化為紅黑樹來處理。

if

(bincount >= treeify_threshold -1)

treeifybin

(tab, hash)

;//把鍊錶轉化為紅黑樹

為了防止雜湊碰撞攻擊,提高map的效率,jdk1.8中加入了紅黑樹,當鍊錶鏈長度為8時,轉成紅黑樹。

1.3 查詢鍵值對

1.4 遍歷

1.5 刪除

public v remove

(object key)

final node

removenode

(int hash, object key, object value,

boolean matchvalue,

boolean movable)

p = e;

}while

((e = e.next)

!= null);}

}// 3. 刪除節點,並修復鍊錶或紅黑樹

if(node != null &&

(!matchvalue ||

(v = node.value)

== value ||

(value != null && value.

equals

(v))))

}return null;

}

【參考文件】

hashmap底層實現原理(上)

hashmap底層實現原理(下)

hashmap 原始碼詳細分析(jdk1.8)

hashmap-----資料結構、常量、成員變數、構造方法

HashMap 底層分析

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

HashMap 底層分析

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

HashMap底層實現

hashmap中定義了乙個node結構,很明顯可以看出這是乙個鍊錶的節點定義。鍵值對key和value以及key的hash值都儲存在這個節點中 static class node implements map.entry hashmap的插入方法在key值已存在的時候是直接替換掉value值,若ke...