1. 程式人生 > >HashMap原始碼之建構函式--JDK1.8

HashMap原始碼之建構函式--JDK1.8

建構函式

變數解釋

  1. capacity,表示的是hashmap中桶的數量,初始化容量initCapacity為16,第一次擴容會擴到64,之後每次擴容都是之前容量的2倍,所以容量每次都是2的次冪
  2. loadFactor,負載因子,衡量hashmap一個滿的程度,初始預設為0.75
  3. threshold,hashmap擴容的一個標準,每當size大於這個標準時就會進行擴容操作,threeshold等於capacity*loadfacfactor

    HashMap(int initialCapacity, float loadFactor)

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegalinitial 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);
}
//建構函式前面都很容易理解,無非是設定正確的initialCapacity和loadFactor
//最後一行呼叫的tableSizeFor(initialCapacity);如下
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;
    }
    

tableSizeFor(...)方法的作用在於找到大於等於initialCapacity的最小的2的冪。n右移一位再同自身進行或操作使得n的最高位和最高位的下一位都為1.

n |= n >>> 1;
比如n=9,則n=1001(2),n-1=100
1001
0100
-----
1100
這是因為或操作的要求為相同位上只要存在1則結果為1,全為0結果才為0.一個數最高位為1,右移一位後的數最高位也為1,不論相對應的位上另一個數是多少結果依舊為1.

而對於n |= n >>> 2n的最高位及其下一位都為1,右移兩位後再進行或操作將使得得到的n的前4位都為1,類似的n |= n >>> 4

使得前8位為1,n |= n >>> 8使得前16位為1。當然並不是說該方法呼叫下來就會使得返回的值是一個32位都為1的數字,這是因為當右移的位數操作實際的有效位數後是不會對數字產生任何變化的。

以n=9為例,在進過兩次右移後n=1111(2)
00000000000000000000000000001111 n (int 32位)
00000000000000000000000000000000 n>>>4
--------------------------------
00000000000000000000000000001111
當n右移4位後全部變成了0,使得結果不會發生任何變化,後面的n |= n >>> 8等等也一樣

在最後返回的時候做了一個判斷(n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1

,正常來說返回的是一個n+1,在前面的操作中已經將前幾位有效的數字變成了1(n=1111),這裡加1使得n=10000(2),即變為大於等於initialCapacity的最小的2的冪,到這裡貌似就差不多了,但是在tableSizeFor(...)方法的第一句有一個減法操作int n = cap - 1;這是為什麼呢?這裡減1實際上是為處理傳入的數本身就是2的冪的情況。比如我傳入的是n=8=1000(2),如果不進行減1操作,得到的則是n=10000(2)=16,實際上我需要的就是8.

HashMap(Map<? extends K, ? extends V> m)

public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

這個建構函式主要是用一個已有的map來構造一個新的HashMap,這裡主要在於putMapEntries(m, false);中。

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

前面幾行是做容錯判斷,當table沒有初始化或者傳入的map容量大於當前map的閾值(threshold),都需要重新進行設定。從for迴圈開始則是將傳入的map的值插入到當前map中。