淺談Map
技術標籤:學習筆記
Map介面
雙列集合,用來儲存一對(key - value)一對的資料
Map介面實現類體系結構
HashMap
雜湊表(hash table)
相當於是一個書架,其實是陣列+連結串列
HashMap底層
陣列+連結串列 (JDK7及之前),陣列+連結串列+紅黑樹(JDK8)
HashMap原始碼分析
HashMap中的幾個屬性
① DEFAULT_INITIAL_CAPACITY : HashMap的預設容量,16
② DEFAULT_LOAD_FACTOR:HashMap的預設載入因子:0.75
③ threshold:擴容的臨界值 = 容量*填充因子:16 * 0.75 = 12,呼叫HashMap的空參構造器, 預設擴容的臨界值為12
⑤ MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64
原始碼
HashMap map = new HashMap(); 在例項化以後,首次呼叫put()底層建立了長度是16的以為陣列Entry[] table
補充
JDK8相較於JDK7在底層實現方面不同:
① new HashMap():底層沒有建立一個長度為16的陣列
② JDK8底層的陣列是Node[],而非Entry[]
③ 首次呼叫put()時,底層建立長度為16的陣列
V put(K key, V value)
判斷hashtable是否存在,如果不存在則建立
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
判斷當前陣列下標對應的元素是否為null,如果是則建立一個Node物件放入
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
如果傳入的key在雜湊表中存在的話,進行資料的替換
if (p.hash == hash &&
((k = p. key) == key || (key != null && key.equals(k))))
e = p;
判斷當前節點是否是紅黑樹
else if (p instanceof TreeNode)
如果連結串列的長度大於8並且陣列長度大於64時,會將連結串列轉成紅黑樹
補充
因為binCount從0開始,所有binCount >= 7即連結串列長度大於8時進入treeifyBin(tab, hash)
for (int binCount = 0; ; ++binCount) {//binCount記錄雜湊桶中連結串列的長度
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) //TREEIFY_THRESHOLD = 8,
treeifyBin(tab, hash);
break;
}
...程式碼省略
在treeifyBin(tab, hash)中雜湊桶的長度小於64就擴容,並不是連結串列長度大於8就變成紅黑樹
所以連結串列的長度大於8並且陣列長度大於64時,才會將連結串列轉成紅黑樹
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)//MIN_TREEIFY_CAPACITY = 64
resize();
為什麼初始長度是2的n次冪
為了保證根據hash計算出索引位置的正確性,底層使用的是 & 位於運算計算出資料在雜湊桶的索引位置
HashMap map = new HashMap(17);
當手動設定Hash Map初始長度不是2的n次冪,呼叫tableSizeFor(int cap)計算出比傳入引數(cap)大的且離他最近的2的n次冪的值,並將該值賦給threshold
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
例如:
如果傳入17,則返回32,如果傳入13,會返回16
結語
筆者處於學習階段,並沒有過多深入探究原始碼(主要是看不懂),該文章如果哪裡有論述不是很清楚的或者是寫錯的還望大佬們糾正,萬分感謝。