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?當放入第乙個元素時,會觸發resize方法,初始化桶陣列容量。即putval()中:計算桶位置時,用
(n-1) & hash
,因為hashmap 中桶陣列的大小 length 總是2的冪,此時,(n - 1) & hash 等價於對 length 取餘(位置不能超過桶長度),舉例:hash = 185,n = 16,餘數 = 9,這裡的hash是由鍵的 hashcode 產生:
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...