HashMap的最大容量為什麼是2的30次方(1左移30)?

sayWhat_sayHello發表於2018-10-18

引言

在閱讀hashmap的原始碼過程中,我看到了關於hashmap最大容量的限制,併產生了一絲疑問。

	/**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two <= 1<<30.
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

為啥最大容量是 1 << 30?

探究過程1 – 為什麼是30

首先是 << 這個操作符必須要理解,在一般情況下 1 << x 等於 2^x。這是左移操作符,對二進位制進行左移。
來看1 << 30。它代表將1左移30位,也就是0010...0
來看這樣一段程式碼:

public static void main(String[] args){
        for (int i = 30; i <= 33; i++) {
            System.out.println("1 << "+ i +" = "+(1 << i));
        }
        System.out.println("1 << -1 = " + (1 << -1));

}

輸出結果為:

1 << 30 = 1073741824
1 << 31 = -2147483648
1 << 32 = 1
1 << 33 = 2
1 << -1 = -2147483648

結果分析:

  1. int型別是32位整型,佔4個位元組。
  2. Java的原始型別裡沒有無符號型別。 -->所以首位是符號位 正數為0,負數為1
  3. java中存放的是補碼,1左移31位的為 16進位制的0x80000000代表的是-2147483648–>所以最大隻能是30

探究過程2 – 為什麼是 1 << 30

探究完1相信大家對 為什麼是30有一點點了解。那為什麼是 1 << 30,而不是0x7ffffffInteger.MAX_VALUE

我們首先看程式碼的註釋

	/**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two <= 1<<30.
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

翻譯一下大概就是:如果建構函式傳入的值大於該數 ,那麼替換成該數。

ok,我們看看建構函式的呼叫:

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;
        this.threshold = tableSizeFor(initialCapacity);
    }

其中這一句:

if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;

看到這有很有疑問了,如果我要存的數目大於 MAXIMUM_CAPACITY,你還把我的容量縮小成 MAXIMUM_CAPACITY???

別急繼續看:在resize()方法中有一句:

if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
}

在這裡我們可以看到其實 hashmap的“最大容量“是Integer.MAX_VALUE;

總結

MAXIMUM_CAPACITY作為一個2的冪方中最大值,這個值的作用涉及的比較廣。其中有一點比較重要的是在hashmap中容量會確保是 2的k次方,即使你傳入的初始容量不是 2的k次方,tableSizeFor()方法也會將你的容量置為 2的k次方。這時候MAX_VALUE就代表了最大的容量值。

另外還有一點就是threshold,如果對hashmap有一點了解的人都會知道threshold = 初始容量 * 載入因子。也就是擴容的 門檻。相當於實際使用的容量。而擴容都是翻倍的擴容。那麼當容量到達MAXIMUM_CAPACITY,這時候再擴容就是 1 << 31 整型溢位。所以Integer.MAX_VALUE作為最終的容量,但是是一個threshold的身份。

相關文章