簡介:
基於雜湊表的 map 介面的實現。此實現提供所有可選的對映操作,並允許使用 null 值和 null 鍵。(除了非同步和允許使用 null 之外,hashmap 類與 hashtable 大致相同。)此類不保證對映的順序,特別是它不保證該順序恆久不變。 此實現假定雜湊函式將元素適當地分布在各桶之間,可為基本操作(get 和 put)提供穩定的效能。迭代 collection 檢視所需的時間與 hashmap 例項的「容量」(桶的數量)及其大小(鍵-值對映關係數)成比例。所以,如果迭代效能很重要,則不要將初始容量設定得太高(或將載入因子設定得太低)
類圖結構:
特性:
1 hashmap 允許key,value都為空。
2 hashmap 在操作鍵值對時不是執行緒安全的。
3 hashmap 底層以陣列+單鏈表(節點數大於8則,單鏈表轉紅黑樹)儲存鍵值對。
4 hashmap實際就是 node table 陣列,node節點實現了map.entry。
主要方法:
1 put 方法詳解:
public v put(k key, v value)
// 根據key計算hash值
static final int hash(object key)
// 根據hash值,將鍵值對存放到hashmap中
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;}}
// 如果存在相同的 key,那就需要將值覆蓋。
v oldvalue = e.value;
if (!onlyifabsent || oldvalue == null)
e.value = value;
afternodeaccess(e);
return oldvalue;}}
++modcount;
// 判斷是否需要進行擴容。
if (++size > threshold)
resize();
afternodeinsertion(evict);
return null;
}
根據key計算陣列下標的步驟如下:
(1)h = key.hashcode():根據key值計算hashcode。這裡的 hashcode() 是乙個 native 方法,根據一定的規則將與物件相關的資訊(比如物件的儲存位址,物件的字段等)對映成乙個數值,這個數值稱作為雜湊值。
(2)h >>> 16:將得到的hashcode無符號右移16位,低位移出(捨棄),高位的空位補符號位,即正數補0,負數補1。
(3 )(h = key.hashcode()) ^ (h >>> 16):h與h右移16位進行異或運算(兩者相等為0,不等為1)得到hash值。
(4)計算(n - 1) & hash 的值即為陣列下標。n代表陣列大小,初始化時預設是16。
2 get 方法詳解:
public v get(object key)
final nodegetnode(int hash, object key) while ((e = e.next) != null);}}
// 為空直接返回
return null;
}
3 resize 方法詳解:
// 初始化或加倍陣列大小 : 如果為null,則分配初始容量。否則,進行2次冪擴充套件。
final node resize()
// 擴容:oldcap*2,即把新的陣列容量 "newcap" 擴大2倍
else if ((newcap = oldcap << 1) < maximum_capacity &&
oldcap >= default_initial_capacity)
// 新的擴容閾值同樣擴大2倍
newthr = oldthr << 1;
}// 當 oldthr > 0 就說明使用者呼叫了有參構造方法(指定了初始容量,並被構造方法 "快取" 到了threshold中了)
else if (oldthr > 0)
newcap = oldthr;
// 初始化的陣列容量為預設的 16,初始化的擴容閾值為預設的 16 * 0.75
else
// 第一次resize()時,計算擴容閾值
if (newthr == 0)
threshold = newthr;
@suppresswarnings()
node newtab = (node)new node[newcap];
table = newtab;
//不是第一次reizie()時
if (oldtab != null)
// 如果判斷不成立,那麼該元素的位址變為 原下標位置+oldcap,也就是lodcap最高位的1,在e.hash對應的位置上也為1,所以擴容後的位址改變了,在後面的**中會放到hihead中,最後賦值給newtab[j + oldcap]
else
} while ((e = next) != null);
if (lotail != null)
if (hitail != null) }}
}}return newtab;
}
上述 (e.hash & oldcap) == 0 即可將原桶中的資料分成2類:元素的位置要麼是在原位置,要麼是「j + oldcap」(當前位置索引+原容量的值)。這也是resize方法擴容後為什麼是原來的2倍的原因。
jdk1.7中,resize時,index取得時,全部採用重新hash的方式進行了。jdk1.8對這個進行了改善:
以前要確定index的時候用的是(e.hash & oldcap-1),是取模取餘,而這裡用到的是(e.hash & oldcap),它有兩種結果,乙個是0,乙個是oldcap,比如oldcap=8,hash是3,11,19,27時,(e.hash & oldcap)的結果是0,8,0,8,這樣3,19組成新的鍊錶,index為3;而11,27組成新的鍊錶,新分配的index為3+8;
jdk1.7中重寫hash是(e.hash & newcap-1),也就是3,11,19,27對16取餘,也是3,11,3,11,和上面的結果一樣,但是index為3的鍊錶是19,3,index為3+8的鍊錶是27,11,也就是說1.7中經過resize後資料的順序變成了倒敘,而jdk1.8沒有改變順序。
HashMap 原始碼深度解析
hashmap 的底層使用 陣列 單項鍊表,jdk1.8後,當鍊表過長時,會將鍊錶轉成紅黑樹,時間複雜度由 o n 變成了 o logn 同時,hashmap 是執行緒不安全的。public v put k key,v value a 對 key 求 hash 值。計算下標 通過 hash obje...
HashMap初步解析
hashmap集合 1 hashmap集合底層是雜湊表 雜湊表的資料結構。2 雜湊表是乙個怎樣的資料結構呢?雜湊表是乙個陣列和單向鍊錶的結合體。陣列 在查詢方面效率很高,隨機增刪方面效率很低。單向鍊錶 在隨機增刪方面效率較高,在查詢方面效率很低。雜湊表將以上的兩種資料結構融合在一起,充分發揮它們各自...
深度了解HashMap
基於hash演算法的圖,是一種資料結構。list set map queue都是資料結構 容器 資料結構 線性結構 陣列 列表list 佇列 棧 樹 二叉樹 b樹 堆 圖。特點 查詢非常快。什麼是hash?雜湊 或音譯雜湊 摘要演算法 把任意長度的輸入,通過雜湊演算法變成固定長度的輸出。不同的輸入,...