1. 程式人生 > >Java原始碼解析HashMap的tableSizeFor函式

Java原始碼解析HashMap的tableSizeFor函式

aka,HashMap的容量大小必須為2的指數,即16,32,64,128這樣的值。那麼,在建構函式中,如果呼叫者指定了HashMap的初始大小不是2的指數,那麼,HashMap的tableSizeFor函式,會計算一個大於或等於給定引數的2的指數的值。先來看一下tableSizeFor函式的原始碼,如下圖。

    /**
     * Returns a power of two size for the given target capacity.
     */
    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;
    }

這裡採用的計算方法不太常見。先是對cap-1,然後一直進行右移操作,最後根據n和MAXIMUM_CAPCITY的大小關係,返回一個值。這究竟是如何實現找到一個大於或等於cap的2的指數的值呢?

首先需要解釋一下>>>符號。>>>是無符號右移操作,即,右移後,高位補0. 例如二進位制的11000101,>>>1後,得到01100010,即不關心符號位,右移後,高位直接補充0. 

還有一個符號是|=,例如n |= n>>>1,這個其實可以翻譯為n = n | n>>>1,| 是位或操作,即兩個數字按位進行或操作,即,某一位上,只有一個數字的該位為1,該位的結果即為1.

說清楚了兩個符號的含義,下面我們開始解釋演算法的過程。

函式一開始,把cap -1 賦值給n。這裡我們先按住不說,稍後回頭解釋。接下來就是對n的四次變換。舉個例,對於

01010000

這個值來說,n>>>1即可得到

00101000

兩個數字位或後,得到

01111000

可以這麼來看這個事情,最開始的n,總有它的最高位為1. 右移1位後,與n進行位或操作,則結果的最高位和次高位都為1了,也就是得到了2個1,而且是高位的2位都為1了。

那麼這時再對n進行n>>>2,再和n進行位或操作,即可得到4個1. 依此類推,n |= n>>>4,即可得到8個1。然後n |= n>>>8,即可得到16個1。然後 n |= n>>>16,即可得到32個1. 當然,後面幾步得到多少個1,得需要n的初始值足夠大才可以。否則,n右移後可能就位0了,那麼在進行位或操作,也只是上一步的值而已。

通過上面的分析,可以知道,進行完n的四次右移然後位或操作後,得到的其實是n的所有為都為1的一個值。那麼最後,返回的時候,取的n + 1,那麼即可得到一個比n大的2的指數的值。

那麼回過頭來看看第一步 n = cap -1就明白了,這裡是為了處理當cap本身即是2的指數時的情況。

因為計算機進行移位和位或操作十分迅速,所以,這個函式的執行效率其實很高。tableSizeFor函式就是這樣快速找到了一個大於等於cap的2的指數的值。