HashMap的初始化大小為什么是2的冪

HashMap的初始化大小為什么是2的冪

首先看下java初始化大小的源碼(代碼來(lái)自jdk1.8)

   //構(gòu)造方法
   public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
       // 這里是初始化的長(zhǎng)度
        this.threshold = tableSizeFor(initialCapacity);
    }
    
    //初始化長(zhǎng)度的方法 
    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;
    }

我們可以看到在初始化長(zhǎng)度的不管我們傳入的是多少,其實(shí)真實(shí)的長(zhǎng)度并不一定使我們傳入的值。它底層進(jìn)行了一些運(yùn)算。這個(gè)運(yùn)算的結(jié)果是比我們傳入的參數(shù)要大,而且是離我們傳入的參數(shù)最近的2的冪的數(shù)。

運(yùn)算原理案例:(假如我們傳入cap=5)

核心原理:從左到右,使用左邊第一個(gè)`1`填充后面的所有位置
    
int n = cap -1;   n=4   00000100
n|= n>>>1;     n做無(wú)符號(hào)右移1位再和n做 `|`運(yùn)算,
               n>>>1:  00000100  -> 00000010
               |n   :  00000100   |
                       00000010   
                    ------------------
                  結(jié)果:00000110
                   
n|=n>>>2;    n做無(wú)符號(hào)右移2位再和n做 `|`運(yùn)算,
               n>>>2:  00000110  -> 00000001 10(后面兩位超出邊界舍棄)
               |n   :  00000110   |
                       00000001   
                    ------------------
                  結(jié)果:00000111
                   
...
      以此類(lèi)推就可以將任何傳入的參數(shù)改變?yōu)楸人蟮亩沂请x它最近的2的冪
                   

為什么初始化的大小必須是2的冪

原因有兩點(diǎn):1.加快哈希運(yùn)算 2.減少哈希沖突

1.加快哈希運(yùn)算

我們都知道比如向hashMap中存入一個(gè)值,通常做法是對(duì)這個(gè)值求hashCode()得到一個(gè)數(shù)hash,然后在用hash對(duì)集合長(zhǎng)度求余數(shù),也就是 hash%length=positon得到的結(jié)果就是存放的位置。

但是求余%的運(yùn)算效率比較低。有沒(méi)有更快的運(yùn)算呢?答案是使用&運(yùn)算。但是使用&運(yùn)算怎么樣才能和使用%效果一樣呢?那就是,當(dāng)HashMap的長(zhǎng)度為2的冪的時(shí)候一下公式就成立了:hash%length==hash&(length-1)。

所以就可以使用&運(yùn)算來(lái)求位置下標(biāo)了。

2.減少哈希沖突,保證數(shù)據(jù)分散

使用2的冪為長(zhǎng)度,則length-1后為奇數(shù),該奇數(shù)轉(zhuǎn)為2進(jìn)制后最后一位肯定是1。

假如長(zhǎng)度為4,則長(zhǎng)度-1為3,再轉(zhuǎn)為2進(jìn)制==0000011,該值與任何hash&運(yùn)算都會(huì)形成==奇數(shù)==或者==偶數(shù)==兩種情況,保證數(shù)據(jù)時(shí)分散的。

可能有人會(huì)想這有什么用?那么我們假如長(zhǎng)度不是4而是3,則3-1為2,再轉(zhuǎn)為2進(jìn)制==0000010,該值與任何hash&運(yùn)算都會(huì)形成==偶數(shù)==,那也就是說(shuō)我的奇數(shù)的下標(biāo)都不能用了。這樣就不僅浪費(fèi)一般的空間,而且增加了hash沖突的概率.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容