HashMap源碼(二) —— 成員變量 DEFAULT_INITIAL_CAPACITY 爲什麼是2的n次方???

HashMap 初始化容量

// 默認容量 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;  //16

爲什麼必須是2的n次冪

 因爲只有是2n,纔可以通過 hash & (leng-1) 計算出的索引儘可能保證數據分佈均勻.
 如果不是2的n次冪,計算出的索引特別容易相同,很容易發生hash碰撞,導致其餘數組空間很大程度上沒有存儲數據,鏈表或者紅黑樹過長,效率較低.

 說明:爲什麼是2的n次方?

2n的二進制是一個首位是1 後面爲是0的數,如 23 二進制爲00001000 23-1 二進制爲00000111
hash & (2n-1) 表示 hash值對應的二進制與n個1 做與運算 都爲1 則爲1 否則爲0
只有當 一個數的二進制有效位全是1的情況下 ,不同hash值計算的結果差異性纔會更多。
如: 以下結果很明顯全爲1的情況計算的差異性會更大
  1111111     100000
&001001    &001001
———————————————
  001001    000000

如果在實例化的時候傳入的值不是2n會怎樣?

當實例化HashMap實例時,如果給定number不是2n initialCapacity(number),由於HashMap的capacity必須是2的次冪,會通過以下方法 (tableSizeFor ) 計算找到大於等於number最小的2n的數。

計算容量分析
HashMap map = new HashMap<>(10)
//獲取大於等於傳入容量的最小二次冪值 
static final int tableSizeFor(int cap) {
        int n = cap - 1;  // n = 9 
        n |= n >>> 1;     // 即 n = n | n >>> 1; 結果13    
        n |= n >>> 2;	  // n = 15
        n |= n >>> 4;	  // n =  
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

// 代碼分析

cap - 1 的操作是爲了規避如果傳入的cap值本身就是2的n次方類型的數時的計算結果
           
	cap = 10;     //     計算過程(二進制)
	n = cap-1;            0000 1001   
———————————————————————————————————————————————
	n |= n>>>1;           0000 1101
						| 0000 0100
					   ————————————————————
					      0000 1101
———————————————————————————————————————————————
	n |= n>>>2;           0000 1101
						| 0000 0011
					   ————————————————————
					      0000 1111
———————————————————————————————————————————————
	n |= n>>>4;           0000 1111
						| 0000 0000
					   ————————————————————
					      0000 1111
———————————————————————————————————————————————
	.......
	對於 0000 1111 這個數右移8位和右移16位都已經沒有任何意義,最終結果都是0000 1111
———————————————————————————————————————————————
	n = n + 1             0000 1111
						+ 0000 0001
					   ————————————————————
					      0001 0000    =    16  
 結論

以上一系列或運算的目的就是將 cap - 1 的二進制有效位全部變成1
最後再用這些有效位是1的二進制+1 ,就會變成一個大於等於傳入容量的最小2的n次冪的數
注:容量最大也就是32bit的正數,因此最後n|n>>>16,最多也是32個1(這是個負數,在執行tableSizeFor之前做了判斷,如果大於最大的則取最大值)

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)    
        initialCapacity = MAXIMUM_CAPACITY;   // 1 << 30
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章