HashMap中tableSizeFor


 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; }

這個方法被調用的地方:

    public HashMap(int initialCapacity, float loadFactor) { /**省略此處代碼**/ this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); }

由此可以看到,當在實例化HashMap實例時,如果給定了initialCapacity,由於HashMap的capacity都是2的冪,因此這個方法用於找到大於等於initialCapacity的最小的2的冪(initialCapacity如果就是2的冪,則返回的還是這個數)。 
下面分析這個算法: 
首先,為什么要對cap做減1操作。int n = cap - 1; 
這是為了防止,cap已經是2的冪。如果cap已經是2的冪, 又沒有執行這個減1操作,則執行完后面的幾條無符號右移操作之后,返回的capacity將是這個cap的2倍。如果不懂,要看完后面的幾個無符號右移之后再回來看看。 
下面看看這幾個無符號右移操作: 
如果n這時為0了(經過了cap-1之后),則經過后面的幾次無符號右移依然是0,最后返回的capacity是1(最后有個n+1的操作)。 
這里只討論n不等於0的情況。 
第一次右移

n |= n >>> 1;

由於n不等於0,則n的二進制表示中總會有一bit為1,這時考慮最高位的1。通過無符號右移1位,則將最高位的1右移了1位,再做或操作,使得n的二進制表示中與最高位的1緊鄰的右邊一位也為1,如000011xxxxxx。 
第二次右移

n |= n >>> 2;

注意,這個n已經經過了n |= n >>> 1; 操作。假設此時n為000011xxxxxx ,則n無符號右移兩位,會將最高位兩個連續的1右移兩位,然后再與原來的n做或操作,這樣n的二進制表示的高位中會有4個連續的1。如00001111xxxxxx 。 
第三次右移

n |= n >>> 4;

這次把已經有的高位中的連續的4個1,右移4位,再做或操作,這樣n的二進制表示的高位中會有8個連續的1。如00001111 1111xxxxxx 。 
以此類推 
注意,容量最大也就是32bit的正數,因此最后n |= n >>> 16; ,最多也就32個1,但是這時已經大於了MAXIMUM_CAPACITY ,所以取值到MAXIMUM_CAPACITY 。 
舉一個例子說明下吧。 

 

 

這個算法着實牛逼啊!

注意,得到的這個capacity卻被賦值給了threshold。

this.threshold = tableSizeFor(initialCapacity);

開始以為這個是個Bug,感覺應該這么寫:

this.threshold = tableSizeFor(initialCapacity) * this.loadFactor;

這樣才符合threshold的意思(當HashMap的size到達threshold這個閾值時會擴容)。 
但是,請注意,在構造方法中,並沒有對table這個成員變量進行初始化,table的初始化被推遲到了put方法中,在put方法中會對threshold重新計算

后面再進行put操作中會使用這個threshold 進行操作

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM