一 HashMap的設計思路
1 存儲:數組+單鏈表 + 紅黑樹(jdk1.8)
(1)數組下標的計算:
// 計算hash值
int h;
(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
// i爲數組下標,n爲數組的容量
i = (n - 1) & hash
第一個計算的設計:通過官方解釋是jvm計算hashcode的低位計算產生衝突的概率大,所以取高位的16位計算
第二個計算的設計:通過&操作保證下標不會超過數組的下標
(2)如果hashcode相同判斷元素是否相同,如果不相同就放到單鏈表中,如果單鏈表的長度超過8個變爲紅黑樹
// 判斷元素相同的邏輯
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
2 擴容
(1)當數組數量超過 容量 X 0.75的時候擴展爲2倍
(2)擴容因子0.75:空間和時間的綜合,如果擴容因子太大導致碰撞的機率增大,也就是增長鏈表的長度,導致了查詢的時間變長;如果擴容因子太小,造成空間浪費
3 hashcode:看Object中的hashCode()方法可知,hashCode()的作用在於提升hash tables的性能。
4 String的hashCode():根據源碼最後可以得到一個多項式,其中的常數31是根據數學上的原理得來的,原因是不太大也不太小的素數相乘得到的數字產生碰撞的概率較小,所以String類型非常適合作爲key值。
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
二 HashTable
1 線程安全版的HashMap
2 存儲、擴容、hashcode的計算不同但是差不多
三 LinkedHashMap
1 用雙鏈表實現
2 查找的時間複雜度是O(n/2),因爲會判斷當前的index更靠近head還是跟靠近tail然後從更近的點開始查找