精妙的位移運算
初始化槽位大小,返回比cap大的最小2的N次方
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;
}
2的次方數特點 只有一個bit位是1 其餘都是0 eg : 0000 1000 -> 8
以9爲例走一遍 9變16====================================開始
1. 9的二進制表示 0000 0000 0000 1001
2. 減1後 ,n = 0000 1000 (高位都是0 忽略掉)
3. 右移一位 0000 0100
4.或運算 0000 1000 >> 0000 1100
5.右移兩位 0000 0011
6.或運算 0000 1100 >> 0000 1111
7.右移四位 0000 0000
8.或運算 0000 1111 >> 0000 1111
9.右移八位 0000 0000
10. 跳過一些 直接到右移動16位或運算 n此時低四位都是1
11.n+1 0001 0000 => 16
9變16====================================結束
走了這麼多,總結下就是把n-1的二進制的 高位是1開始後面的值全部設置成0(含第一個高位是1的那位),高位往前一位設置成1
減1是爲了兼容本身就是2的N次方的數
看下5到8 9到16可以清晰看出來
5 0000 0101
8 0000 1000
9 0000 1001
16 0001 0000
原始的計算槽位
(n-1)& hash n是數組的長度 (2的次方數 ), hash是key的經過處理的hash(處理看下面那一小段)
這個與運算比較簡單了 容量是16位的數組 n-1 就是 0000 0000 0000 1111 與出來的值最多是15.管你是啥 高位全部歸零
簡單說下對這個hash的處理
(h=key.hashCode()) ^ (h>>>16)
h右移16位 主要讓高位右移 減少hash碰撞[想下 如果低位都是0 那效果估計很糟糕 瘋狂碰撞]
rehash計算槽位
newTab[e.hash & (newCap - 1)] = e;
具體自己畫圖領悟吧,說下結論 擴容是oldCap<<1 即容量擴大了一倍
老元素的key.hash&操作後 ,兩種情況【自己要看原來的hash在擴容的哪一位上是不是1】
1.老的key的低N位如果是0那還是在老的槽位,
2.低N位如果是1 新座標= 老座標 + OldCap
舉個通俗的例子 默認大小是16個槽位,擴容後變成32個槽位
假設x的hash是(0000 0010)在老數組的2個位置,擴容散列後 它不受影響還是在第二個位置 (他沒有潛力 他的高位全是0)
y的hash是 0001 1111 原來他的index是15 擴容後它到了31個
0000 1111 (這個是16位的cap-1)
0001 1111 (這個是32位的cap-1)
擴容機制
前置
hashMap的所有元素量>=擴容閥值&數組位置已經有值 1.7的邏輯,1.8裏面把第二個條件移除
具體騷操作
jdk1.7 主要是正常插入的時候是尾插,rehash的時候是頭插
jdk1.0 都是尾插,具體代碼如下 用了兩個鏈表
Node<String,String> loHead = null, loTail = null;
Node<String,String> hiHead = null, hiTail = null;
Node<String,String> next;
do {
next = e.next;
System.out.println("hash-" + e.hash + "& oldCap" + (e.hash & oldCap));
System.out.println(oldCap);
// 1111001100000001110101110000
// 0000000000000000000000001000
//低位是0的 用loHead裝配
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
fail-fast細節
for循環會檢測modCount和exceptedModCount,每次變更都會增加modCount 不一致就會報錯