1. 程式人生 > >java8 HashMap介面實現

java8 HashMap介面實現

一、類屬性

    /**
     * 預設的初始容量是16,必須是2的倍數
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 最大容量
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 預設負載因子
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 桶內節點數大於8個時,儲存結構由單向連結串列轉換成紅黑樹
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 擴容時當桶內節點數小於6個時紅黑樹轉換成單向連結串列
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     *桶儲存結構由列表轉換成樹時陣列的最低容量,低於該容量時通過擴容陣列解決部分桶節點過多問題
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /* ---------------- Fields -------------- */

    /**
     *儲存Node的陣列,總是2的整數次冪
     */
    transient java.util.HashMap.Node<K,V>[] table;

    /**
     *儲存元素的Set集合
     */
    transient Set<Map.Entry<K,V>> entrySet;

    /**
     * 包含的鍵值對個數
     */
    transient int size;

    /**
     *記錄修改次數,實現在遍歷map時如果修改map會快速失敗的功能
     */
    transient int modCount;

    /**
     * 進行擴容的臨界值,根據初始容量計算出來的實際最大容量再乘以負載因子
     */
    int threshold;

    /**
     * 負載因子
     */
    final float loadFactor;

二、靜態方法

    /**
     * 該方法用於計算key的hash值,然後用該hash值對HashMap的容量取模,算出key儲存的位置
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

為什麼要做上述的位運算了?首先實際生產中HashMap的容量基本不會超過2^16,即65536個,這種大Map通常都放到快取元件中了,所以用key的hash值的低16位是最關鍵的。其次,hashCode()方法返回的是一個int型別,最長32位,右移16位且高位補0(h>>>16)後與原值做異或(兩者相同為0,不同為1),相當於高16位和低16位都參與了運算,與原來的只有低16位參與運算相比可以高效的避免hash值不同最後取模結果一樣的情形,使key的分散更均勻,提高查詢效率。參考如下例子:

keyA hashCode  0000 0000 0000 1100 1010 1111 0000 1000 

keyB hashCode  0000 0000 0000 0011 1010 1111 0000 1000 

HashMap容量n    0000 0000 0000 0000 0010 0000 0000 0000
          n-1   0000 0000 0000 0000 0001 1111 1111 1111

只有低16位參與計算時,hashCode對容量取模的結果都是:
               0000 0000 0000 0000  0000 1111 0000 1000

當高16位參與運算時:
keyA h>>>16     0000 0000 0000 0000 0000 0000 0000 1100
     ^運算       0000 0000 0000 1100 1010 1111 0000 1100
     取模        0000 0000 0000 0000 0000 1111 0000 1100

keyB h>>>16     0000 0000 0000 0000 0000 0000 0000 0011
      ^運算      0000 0000 0000 0011 1010 1111 0000 1011
      取模        0000 0000 0000 0000 0000 1111 0000 1011

最終取模的結果不一樣
   /**
     * 該方法用於計算HashMap的實際容量,cap為使用者輸入的初始容量,實際容量必須是最近的大於或者等於初始容量的2的整數次冪,比如10,則返回16
    */
  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;
        //n最大可能為2^31-1,所以先判斷是否大於2^30,如果不是才能加1,否則會超過int型別的最大值
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

參考如下計算過程:

初始值
00000000 11000000 00000000 00010011
n-1
00000000 11000000 00000000 00010010
>>>1
00000000 01100000 00000000 00001001
|=
00000000 11100000 00000000 00011011
>>>2
00000000 00111000 00000000 00000110
|=
00000000 11111000 00000000 00011111
>>>4
00000000 00001111 10000000 00000001
|=
00000000 11111111 10000000 00011111
>>>8
00000000 00000000 11111111 10000000
|=
00000000 11111111 11111111 10011111
>>>16
00000000 00000000 00000000 11111111
|=
00000000 11111111 11111111 11111111
n+1
00000001 00000000 00000000 00000000

從上述計算過程可以發現,其實初始值的低位是沒有用到的,關鍵是初始值的最高位的1,每次右移再做或運算相當於不斷的在最高一位1的後面不斷填充1,第一次右移1位,填充1個1,第二次右移2位,填充2個1,第三次右移4位,填充4個1,第四次右移8位,填充8個1,最後一次右移16位,填充16個1,如果最高位的1的後面都是1,則後面的位運算無意義。最後通過加1,算出結果。為什麼不會最多會右移16位了?因為int是32位,右移16位最多填充31個1,當最高位的1是第一位時,也能讓後面都填充為1。 

    /**
     * 如果x實現了Comparable介面則返回x的Class,否則返回null
     */
    static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            //獲取該類實現的介面
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    //如果該類實現了Comparable介面,並且介面的引數化型別就是該類本身
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }

    /**
     *k和x進行比較,要求k實現Comparable介面,x的Class型別就是kc
     */
    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
    static int compareComparables(Class<?> kc, Object k, Object x) {
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x));
    }
ParameterizedType介面參考:https://www.cnblogs.com/linghu-java/p/8067886.html,參考如下用例:
public  class TypeTest<S extends Cloneable> {

    private Map<String,Integer> map;

    private List<? extends Number> numbers;
    private List<? super HashMap> numbers2;

    private List<S> list;

    private List<String>[] arrays;
    

    @Test
    public void name() {
        List<String> list=new ArrayList<>();
        list.add("test");
        //獲取該類實現的介面
        Type[] types=list.getClass().getGenericInterfaces();
        for(Type type:types){
            System.out.println(type);
            if(type instanceof ParameterizedType){
                ParameterizedType parameterizedType=(ParameterizedType) type;
                System.out.println("param:"+ Arrays.toString(parameterizedType.getActualTypeArguments()));
                System.out.println("param:"+parameterizedType.getRawType());
                System.out.println("param:"+parameterizedType.getOwnerType());
                System.out.println("param:"+parameterizedType.getTypeName());
            }
        }
    }


    @Test
    public void test3() throws Exception{
        Field field=TypeTest.class.getDeclaredField("map");
        //通過類屬性獲取該屬性的引數化型別
        ParameterizedType parameterizedType=(ParameterizedType) field.getGenericType();
        System.out.println(Arrays.toString(parameterizedType.getActualTypeArguments()));
        System.out.println(parameterizedType.getTypeName());
        System.out.println(parameterizedType.getRawType());
        System.out.println(parameterizedType.getOwnerType());
    }

    @Test
    public void test4() throws Exception {
        Field field=TypeTest.class.getDeclaredField("numbers");
        ParameterizedType parameterizedType=(ParameterizedType) field.getGenericType();
        System.out.println(parameterizedType.getRawType());
        Type[] types=parameterizedType.getActualTypeArguments();
        WildcardType wildcardType=(WildcardType) types[0];
        System.out.println(Arrays.toString(wildcardType.getLowerBounds()));
        System.out.println(Arrays.toString(wildcardType.getUpperBounds()));
    }

    @Test
    public void test5() throws Exception {
        Field field=TypeTest.class.getDeclaredField("list");
        ParameterizedType parameterizedType=(ParameterizedType) field.getGenericType();
        System.out.println(parameterizedType.getRawType());
        Type[] types=parameterizedType.getActualTypeArguments();
        TypeVariable typeVariable=(TypeVariable) types[0];
        System.out.println(Arrays.toString(typeVariable.getBounds()));
        System.out.println(typeVariable.getName());
    }

    @Test
    public void test6() throws Exception {
        Field field=TypeTest.class.getDeclaredField("arrays");
        GenericArrayType genericArrayType=(GenericArrayType) field.getGenericType();
        Type type=genericArrayType.getGenericComponentType();
        System.out.println(type);
        ParameterizedType parameterizedType=(ParameterizedType) type;
        System.out.println(Arrays.toString(parameterizedType.getActualTypeArguments()));
        System.out.println(parameterizedType.getRawType());
    }

}

三、類構造器方法

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

 public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

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

在未指定容量的時候threshold並未初始化,所以情況下都初始化了負載因子。

四、Map介面實現

1、put方法,增加key/value

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * 插入元素
     * @param hash key的hash值
     * @param key
     * @param value
     * @param onlyIfAbsent 如果為true且該key存在的時候則不改變原值
     * @param evict  如果為false,則表為建立模式
     * @return key的原值,如果key不存在返回null
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        java.util.HashMap.Node<K,V>[] tab; java.util.HashMap.Node<K,V> p; int n, i;
        //如果tab未初始化,通過resize()方法初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //(n - 1) & hash 相當於hash對n-1求餘
        //如果目標tab內沒有元素
        if ((p = tab[i = (n - 1) & hash]) == null)
            //將新元素放入tab類
            tab[i] = newNode(hash, key, value, null);
        else {
            //如果目標tab內有元素
            java.util.HashMap.Node<K,V> e; K k;
            //判斷tab內的頭元素是否等於目標元素
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
                //如果tab內元素是樹形結構
            else if (p instanceof java.util.HashMap.TreeNode)
                e = ((java.util.HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //如果tab內元素是鏈式結構,binCount用於記錄連結串列長度
                for (int binCount = 0; ; ++binCount) {
                    //連結串列所有元素都遍歷完了
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //連結串列元素個數超過閾值則轉化成樹形結構
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //判斷連結串列上的元素是否跟目標元素一致
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //如果已經存在這個key
            if (e != null) {
                V oldValue = e.value;
                //設定新值
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                //執行訪問回撥動作
                afterNodeAccess(e);
                return oldValue;
            }
        }
        //新插入了一個key,modCount和size都加1
        ++modCount;
        //當size大於閾值後進行擴容
        if (++size > threshold)
            resize();
        //執行插入完成回撥
        afterNodeInsertion(evict);
        return null;
    }

 2、get方法, 根據key查詢

 /**
     * 返回值為null,不一定不包含這個key,也可能key對應的value就是null
     */
    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

  
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //效驗tab不為null且目標tab內頭元素不為空
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //判斷第一個Node節點是否是目標key
            if (first.hash == hash &&
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            //如果第一個不是則檢查該Node節點的下一個節點
            if ((e = first.next) != null) {
                //如果該節點是TreeNode
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                //如果是普通的單向連結串列節點,則遍歷所有的節點直到找到目標key
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

3.resize方法,初始化陣列或者執行擴容

   /**
     *
     * 執行table初始化或者擴容,擴容都是當前容量的雙倍
     *
     * @return the table
     */
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            //已經達到最大容量
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //當前容量乘以2小於最大容量則擴容,否則維持不變
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        //已經設定容量尚未初始化
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        //初始容量未設定,採用預設值初始化
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }

        //根據newCap計算newThr
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }

        //初始化newCap對應的Node陣列
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

        //進行兩倍擴容
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    //該Tab內只有一個元素
                    if (e.next == null)
                        //將該元素重新hash,最終計算的索引有可能變,也可能不變
                        newTab[e.hash & (newCap - 1)] = e;
                    //如果該Tab記憶體儲的是一個紅黑樹
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    //如果該Tab記憶體儲的是一個連結串列
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;

                        //迴圈遍歷將原連結串列拆分成兩個
                        do {
                            next = e.next;
                            //因為Cap都是2的整數倍,所以最終的結果只能是0或者2的整數倍,等於0時按擴容後的容量重新計算index結果還是原來的,
                            //如果等於2的整數倍,則重新計算index的結果需要在原來的基礎上加上原有的1倍容量
                            //以原有的容量16擴容成32為例,hash值為23,容量16時計算的index為7,容量為32時計算的index為23,hash值為7時,容量為16或者32時index都是7
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        //
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

4、remove,刪除某個key

 public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

    
    final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        //效驗目標tab是否為null
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; K k; V v;
            //找到指定key對應的元素,查詢邏輯和getNode方法一致
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            //如果目標key對應的元素存在
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                //如果是樹形結構
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                //如果為頭元素
                else if (node == p)
                    tab[index] = node.next;
                //對鏈式結構,p是node的上一個元素
                else
                    p.next = node.next;
                ++modCount;
                --size;
                //執行元素刪除回撥
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

5、treeifyBin方法,將單向連結串列轉換成紅黑樹結構

final void treeifyBin(java.util.HashMap.Node<K,V>[] tab, int hash) {
        int n, index; java.util.HashMap.Node<K,V> e;
        //如果容量小於64則只是做擴容
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
            //效驗目標tab內元素是否為空
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            //hd表示頭部元素,tl表示連結串列中上一個元素
            java.util.HashMap.TreeNode<K,V> hd = null, tl = null;
            do {
                //將原來的Node元素轉換為TreeNode
                java.util.HashMap.TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            //將已經是連結串列的TreeNode轉化成一個紅黑樹
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

6、HashIterator實現,該類是KeyIterator,ValueIterator,EntryIterator的父類,如果通過Iterator實現對應的KeySet,Values,EntrySet,從而實現對key,value,Map.Entry的遍歷。

abstract class HashIterator {
        Node<K,V> next;        // next entry to return
        Node<K,V> current;     // current entry
        int expectedModCount;  // for fast-fail
        int index;             // current slot

        HashIterator() {
            expectedModCount = modCount;
            Node<K,V>[] t = table;
            current = next = null;
            index = 0;
            if (t != null && size > 0) {
                //遍歷找到Tab內第一個不為空的元素
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            //先判斷當前節點是否存在下一個元素,即在同一個Tab類遍歷,如果沒有則找到下一個不為空的Tab遍歷
            //注意此處返回了當前節點,並找到了下一個遍歷的節點
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            //次數改變了expectedModCount,所以刪除元素不會快速失敗
            expectedModCount = modCount;
        }
    }

    final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }

    final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
    }

    final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }

另外還有一個java8引入的Spliterator介面,用於表示該類可以通過stream的方式並行處理流資料,參考如下:

https://blog.csdn.net/lh513828570/article/details/56673804,介面實現方式基本是固定的

7、Map序列化,HashMap改寫了預設實現,注意readObject的順序必須與writeObject的順序保持一致

private void writeObject(java.io.ObjectOutputStream s)
        throws IOException {
        int buckets = capacity();
        //表示開始寫入物件
        s.defaultWriteObject();
        s.writeInt(buckets);
        s.writeInt(size);
        internalWriteEntries(s);
    }

    // Called only from writeObject, to ensure compatible ordering.
    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        java.util.HashMap.Node<K,V>[] tab;
        if (size > 0 && (tab = table) != null) {
            for (int i = 0; i < tab.length; ++i) {
                for (java.util.HashMap.Node<K,V> e = tab[i]; e != null; e = e.next) {
                    //遍歷tab將tab內每個元素的key和value分開寫入物件流中
                    s.writeObject(e.key);
                    s.writeObject(e.value);
                }
            }
        }
    }

    /**
     * Reconstitute the {@code HashMap} instance from a stream (i.e.,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        // 表示開始從流中讀取物件
        s.defaultReadObject();
        //HashMap初始化
        reinitialize();
        //校驗負載因子
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new InvalidObjectException("Illegal load factor: " +
                    loadFactor);
        //讀取buckets,即原Map的容量
        s.readInt();
        // 讀取原Map的size
        int mappings = s.readInt();
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " +
                                             mappings);
        else if (mappings > 0) { // (if zero, use defaults)
            //計算容量
            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
            float fc = (float)mappings / lf + 1.0f;
            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                       DEFAULT_INITIAL_CAPACITY :
                       (fc >= MAXIMUM_CAPACITY) ?
                       MAXIMUM_CAPACITY :
                       tableSizeFor((int)fc));
            float ft = (float)cap * lf;
            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                         (int)ft : Integer.MAX_VALUE);

            // Check Map.Entry[].class since it's the nearest public type to
            // what we're actually creating.
            SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
            //tab初始化
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
            table = tab;

            // 逐一讀取key,value
            for (int i = 0; i < mappings; i++) {
                @SuppressWarnings("unchecked")
                    K key = (K) s.readObject();
                @SuppressWarnings("unchecked")
                    V value = (V) s.readObject();
                putVal(hash(key), key, value, false, false);
            }
        }
    }