[java基礎-hashMap] capacity/算槽位等有意思的操作

精妙的位移運算

初始化槽位大小,返回比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 不一致就會報錯

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章