天天看點

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

注意:HashMap的源碼本文僅分析JDK1.7,1.8版本的請參考: JDK1.8版本HashMap

JDK1.7版本HashMap

1 結構圖

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

HashMap由數組(本文後續以table表示)+ 連結清單構成。其本意是用table來存儲一個鍵值對(本文後續以Entry表示),然後可以通過key取hash值同時對數組長度取模,散列分布到數組每個下标下的時候,那取值時間複雜度将為O(1),在最理想的情況下,就是數組一個下标上隻存一個Entry值。

但是天不如人願,并不存在一個如此優秀的Hash函數,讓每個Entry的key一定能散列分布在數組每個下标下,總是會出現如下情況:不同的key值,取hash值并取模後,數組下标值一樣,那這個時候就出現了Hash沖突。解決Hash沖突方法有挺多種的,而HashMap采用連結清單來解決,意思是,假如出現Hash沖突後,則在該下标下,形成一個單連結清單,存儲不同的Entry,那麼在查詢操作的時候,就需要對連結清單進行周遊,同時調用HashMap的key的equals方法,判斷其是否相等,相等則傳回該Entry,這也就是HashMap為什麼要求key對象一定要實作hashCode方法和equals方法的原因。

2 繼承關系

我們來看下HashMap的繼承關系圖:

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

左邊實作的Cloneable接口和Serializable接口是為了實作克隆和序列化功能,非本文重點關注對象,請忽略。HashMap主要繼承父類AbstractMap,而父類實作了Map接口。

2.1 Map接口

下面是Map接口提供的方法:

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

可以看到Map還有一個内部接口Entry,Entry就是代表鍵值對,一個key和一個vale值。

2.2 AbstractMap抽象類(非重點父類,請稍微浏覽即可)

AbstractMap對于HashMap的作用,僅僅是提供了keySet 和values 變量。而其他的Map方法都在HashMap中重寫了一遍。是以請看淡AbstractMap抽象父類。

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

AbstractMap.java(省略了其實作的方法):

public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }
    ...
    public abstract Set<Entry<K,V>> entrySet();
           

以上兩個方法是AbstractMap未實作Map接口的方法。其它方法,AbstractMap基本都實作了。

2.3 HashMap類

HashMap雖然繼承了AbstractMap類,但實際上,它基本上重寫了AbstractMap類實作Map接口的方法,換句話說,他表面上繼承AbstractMap,但暗地裡把它實作的方法都重寫了一遍,絲毫不留情面。其隻使用了父類的兩個成員變量keySet和values

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

2.3.1 字段

/**
     * 預設初始化容量
     */
    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;

    /**
     * 空table
     */
    static final Entry<?, ?>[] EMPTY_TABLE = {};

    /**
     * table,用來存放鍵值對,鍵值對可以單個值,也可以是一個連結清單
     */
    transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;

    /**
     * 大小
     */
    transient int size;

    /**
     * 門檻值,擴容的主要判斷條件之一
     */
    int threshold;

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

    /**
     * 結構更改次數的統計,增、删都會加1。也是快速失敗的觸發依據,後面HashIterator會介紹如何使用該值
     */
    transient int modCount;

    /**
     * 預設備選hash門檻值
     */
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

    /**
     * 注意、注意、注意:
     * 這兩個成員變量來自父類AbstractMap,由于其為default類型,在不同包将通路不到,是以在此聲明,以此代替
     * 由于keySet、value在父類中的keySet()、values()、clone()方法中用到,而HashMap這幾個方法全部重寫了,是以不會影響使用
     */
    transient volatile Set<K> keySet = null;
    transient volatile Collection<V> values = null;
           

2.3.2 構造器

最重要的構造器:

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);
        // 初始化負載因子和門檻值
        // 注意:門檻值在這裡并非穩定值,在put方法進行初始化後,會等于數組長度*負載因子的值
        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        // init是空實作,忽略
        init();
    }
           

接收容量的構造器:

// 接收容量的構造器,并将負載因子設定為預設的0.75
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
           

最常用的構造器,無參構造:

// 用得最多的構造器,預設容量16、負載因子0.75
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }
           

接收一個map的構造器:

public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    }
           

2.3.3 get(Object key)方法的實作原理

// 取值
    public V get(Object key) {
        // 由于HashMap支援一個null值,因為key為null的時候,作特殊處理
        // null的值固定在table[0]下
        if (key == null)
            return getForNullKey();  // 代碼1
        // 取entry
        Entry<K, V> entry = getEntry(key);  // 代碼2

        return null == entry ? null : entry.getValue();
    }
           

由于HashMap支援key=null,而對于key=null的情況,由代碼1進行特殊處理,下面是其對應邏輯:

// 取key=null的值
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
           

周遊table[0]下标下的值,由于key=null的特殊值,固定會放在table[0]下。假如table[0]下的結點,有key為null的,則将該值傳回。

當key不為null時,由代碼2取key對應的Entry,下面看看getEntry的實作邏輯:

// 擷取鍵值對
    final Entry<K, V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        // 計算key的hash值
        int hash = (key == null) ? 0 : hash(key);
        // 周遊某個下标下的連結清單(如果是連結清單的話)
        for (Entry<K, V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            // 如果key的hash相同 && (key相同或者key的equals方法的值相同)則傳回
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
           

該方法分兩步,首先根據key,計算該key對應的下标值。

第二步,周遊下标對應的連結清單,然後分别判斷其hash值是否相等且(key相同或者key的equals方法相等)則将該值傳回。

2.3.4 put(K key, V value)方法的實作原理

// 添加值
    public V put(K key, V value) {
        // 如果table為空,則根據門檻值初始化table的值。
        // 由于HashMap是延遲初始化的,構造器隻給table指派為EMPTY_TABLE
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);  // 代碼3
        }
        // 特殊處理key=null的情況
        if (key == null)
            return putForNullKey(value);	// 代碼4
        // 計算hash值
        int hash = hash(key);	// 代碼5
        // 根據hash值與table長度,取模(實際為與操作)得到下标值
        int i = indexFor(hash, table.length);  // 代碼6
        // 判斷在目前下标下的連結清單有沒有相同key值的Entry,有則覆寫,并将舊值傳回
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        // 假如沒有相同key值,則将為HashMap增加一個新值
        // 由于結構發生改變,是以modCount需要+1
        modCount++;
        // 添加新的Entry值
        addEntry(hash, key, value, i);  // 代碼7
        return null;
    }
           

從上面構造器可以發現,HashMap是延遲初始化的,最開始的時候,給table指派了一個EMPTY_TABLE,但真正将值設定進來的時候,才去申請capacity對應的空間。

代碼3,當table==EMPTY_TABLE的時候,将執行初始化操作,下面代碼看看其如何初始化的:

// 初始化table的值
    private void inflateTable(int toSize) {
        // 将容量控制為2的n次方
        int capacity = roundUpToPowerOf2(toSize);

        // 門檻值載不大于MAXIMUM_CAPACITY+1的情況下,值等于capacity*loadFactor
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        // 根據容量,建立Entry數組
        table = new Entry[capacity];
        // 初始化hash種子,可能會對hashSeed字段指派,hashSeed可能會影響hash(Object key)方法的計算,下面會講述
        initHashSeedAsNeeded(capacity);
    }
           

回到put方法的代碼,在完成table值的初始化後,往下執行到代碼4,會對key==null的情況進行特殊處理:

// 特殊處理key=null的值,将其放在table[0]下
    private V putForNullKey(V value) {
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        // 添加鍵值對
        addEntry(0, null, value, 0);
        return null;
    }
           

如果key!=null的時候,計算key對象的hash值,并取模得到key對應table的下标值,下面統一對于代碼代碼5和代碼6進行分析:

代碼5:

// 對key值作hash運算
    final int hash(Object k) {
        // hashSeed在大部分情況下是0,當容量超過2147483647後,可能會變為一個随機hashSeed(該情況不做分析)
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        // h為0的時候,下面異或操作結果将是 右邊操作數的值,則下面相當于h = k.hashCode()
        h ^= k.hashCode();

        // 以下代碼使用了擾動計算,異或了高位+低位的值,避免高位沒有參與計算,導緻在與table-1做與運算的時候,無法散列在各個下标值下
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
           

在hashSeed不為0的時候且key為String類型的時候,可能會調用Hashing.stringHash32進行特殊處理,來的到hash值。

以上情況不滿足的時候,将key的hashCode指派給h,然後通過異或h的多個比特位進行計算。這裡用了擾動計算,将hashCode的每個值都用到計算中,以支援散列分布。

代碼6:

// 擷取目前key下落在table哪個下标,用與運算,等效取模,但性能比取模快
    static int indexFor(int h, int length) {
        // 首先,length要求為2的n次方,2的n次方-1的時候,得到一個二進制是全1的值
        // 當h的值去與操作一個全1的值,等效于對 %length
        // 那為什麼不用取模操作,而要與操作呢?因為與操作效率比取模操作速度快很多
        return h & (length - 1);
    }
           

回到put方法,在計算取得下标值後,将判斷目前添加的鍵值對key是否已存在,開始周遊儲存在該下标下的連結清單,當結點的hash值相等且(key相同或者key的equals方法相等)則覆寫該值,并将舊值傳回。

如果以上情況都不滿足,則說明目前添加的Entry既不是key==null,又不是key存在的情況。那麼開始新增一個鍵值對,然後傳回null。該操作将可能觸發擴容。

代碼7:

// 添加新的鍵值對
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // 判斷目前容器大小是否大于等于門檻值 && 目前table下标的值存在值,将進行擴容操作
        if ((size >= threshold) && (null != table[bucketIndex])) {
            // 擴容為目前table長度的兩倍
            resize(2 * table.length);  // 代碼8 
            // 重新計算擴容後,目前key将位于table的哪個下标
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        // 建立鍵值對
        createEntry(hash, key, value, bucketIndex);
    }
           

代碼8的擴容方法,在下面講述,将解析其為什麼在多線程環境下會形成環路。

2.3.5 resize()方法原理及多線程環境下環路的形成原因

// 擴容方法,多線程不安全,其他方法也是多線程不安全,但是resize問題比較嚴重
    // 可能形成環形連結清單,造成死循環,CPU100%
    void resize(int newCapacity) {
        // 暫存舊的table
        Entry[] oldTable = table;
        // 暫存舊table長度
        int oldCapacity = oldTable.length;
        // 假如舊的table長度等于最大容量,将不做擴容操作
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        // 根據新容量,建立新table
        Entry[] newTable = new Entry[newCapacity];
        
        // 執行table上面值得轉移,将oldTable上面的值轉移到newTable,會重新hash并取模來判斷新的下标值
        // 多線程環境下,下面方法可能會産生環形連結清單,造成死循環
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        // 将newTable 替換為table
        table = newTable;
        // 重新計算門檻值 ,控制門檻值不能超過MAXIMUM_CAPACITY+1
        threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
           

在進行newCapacity(新容量)的合法值校驗完成後,建立一個新的Entry數組,然後開始調用transfer進行元素的轉移,意思就是把舊數組上面的元素值,進行重新hash并取模,确定其在新數組的下标,我們來看看其如何實作:

// 轉移值
    void transfer(Entry[] newTable, boolean rehash) {
        // 擷取新的容量
        int newCapacity = newTable.length;
        // 周遊table的Entry
        for (Entry<K, V> e : table) {
            // 周遊連結清單(如果是連結清單的話)
            while (null != e) {
                // 假如線程A取到next,然後時間片用完,CPU切換到線程B繼續執行連結清單值轉移操作,并執行完畢
                // 那麼線程A有了CPU時間片後,繼續執行,此時再觸發轉移值操作,很容易會形成環形連結清單,造成死循環
                Entry<K, V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                // 根據hash值取模,得到在新table的下标值
                int i = indexFor(e.hash, newCapacity);
                
                // 采用頭插法,将目前節點下一個節點指向newTable目前下标的值
                e.next = newTable[i];
                // 将目前節點作為newTable的頭結點
                newTable[i] = e;
                // 将下一個節點指派給下一輪循環的結點,繼續往下執行循環
                e = next;
            }
        }
    }
           

這裡注意一點是,newTable是作為方法參數傳遞進來,每個線程都會建立一個新的newTable,其值是線程私有,而線程不安全的變量,其實是table裡面的Entry。具體如何成環路的場景分析,請參考下面的示例圖。

JDK1.7版本HashMap的源碼分析JDK1.7版本HashMap

圖檔位址: https://www.processon.com/diagraming/5e06f27ce4b0125e2924abba

2.3.5 keySet()方法的實作原理

public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }
           

上述代碼為keySet實作原理,很簡單,就是在keySet為null的時候,new了一個KeySet對象,那下面我們看看KeySet對象:

private final class KeySet extends AbstractSet<K> {
		// 實作Set的疊代方法
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        // 大小值跟HashMap大小一緻
        public int size() {
            return size;
        }
        // 調用HashMap的containsKey方法
        public boolean contains(Object o) {
            return containsKey(o);
        }
        // 調用HashMap的removeEntryForKey方法
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        // 調用HashMap的clear方法
        public void clear() {
            HashMap.this.clear();
        }
    }
           

由上述代碼看出,其具體實作,全都依賴于HashMap。這裡可能有個困惑的點,就是我們看到一個Set對象,就會想當然覺得裡面的元素都是存儲在Set對象裡面,其實不然。KeySet對象的值,如元素的值,集合大小等資訊,其實都是用着HashMap的成員域。如果你擷取到KeySet對象後,調用了remove方法,實際上會改變該key對應在HashMap中的Entry。

我們分析一下iterator方法:

public Iterator<K> iterator() {
			// 	調用疊代器方法時,會建立一個新的Key疊代器
            return newKeyIterator();
    }
           

調用HashMap的方法newKeyIterator建立一個KeyIterator對象。

Iterator<K> newKeyIterator()   {
		// 每次調用都會建立一個新的KeyIterator
        return new KeyIterator();
    }
           

KeyIterator 類:

private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
           

其next方法是調用父類HashIterator的nextEntry方法擷取一個Entry,然後再擷取Entry的key,這跟ValueIterator的實作是對應的。

下面我們看看nextEntry方法的具體實作:

private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K,V> next;        // 需要傳回的下一個Entry
        int expectedModCount;   // 快速失敗的判斷依據,如果該值不等于HashMap的modCount,将抛出ConcurrentModificationException
        int index;              // 目前疊代下标
        Entry<K,V> current;     // 目前Entry

        HashIterator() {
        	// 将HashMap結構修改次數儲存一個副本,下面疊代的時候,如果兩個值不相等,将抛出并發修改異常(其實不太準确,下面案例分析)ConcurrentModificationException
            expectedModCount = modCount;
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                // 周遊table,直到定位到table不為null的值
                while (index < t.length && (next = t[index++]) == null);
            }
        }

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

        final Entry<K,V> nextEntry() {
        	// 判斷在執行疊代器期間,HashMap有沒有對結構進行修改
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
            current = e;
            return e;
        }

        public void remove() {
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }
           

從HashIterator的代碼我們可以看出,在其成員域中維護了

3 源碼

下面是源碼,篇幅較長,上文已将重要方法都講解了,如果沒有興趣,可以不看。篇幅長,是以可能會比較卡!

package hashmap1_7;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.util.*;

/**
 * JDK1.7版本,加入個人一些了解、注釋
 * 當使用該類的時候,記得要把項目的JDK版本切換為1.7,否則會報錯
 * copy form jdk1.7
 * @author wzj
 * 2019/12/28
 */
public class HashMap<K, V>
        extends AbstractMap<K, V>
        implements Map<K, V>, Cloneable, Serializable {

    /**
     * 預設初始化容量
     */
    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;

    /**
     * 空table
     */
    static final Entry<?, ?>[] EMPTY_TABLE = {};

    /**
     * table,用來存放鍵值對,鍵值對可以單個值,也可以是一個連結清單
     */
    transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;

    /**
     * 大小
     */
    transient int size;

    /**
     * 門檻值,擴容的主要判斷條件之一
     */
    int threshold;

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

    /**
     * 結構更改次數的統計,增、删都會加1。也是快速失敗的觸發依據,後面HashIterator會介紹如何使用該值
     */
    transient int modCount;

    /**
     * 預設備選hash門檻值
     */
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

    /**
     * 注意、注意、注意:
     * 這兩個成員變量來自父類AbstractMap,由于其為default類型,在不同包将通路不到,是以在此聲明,以此代替
     * 由于keySet、value在父類中的keySet()、values()、clone()方法中用到,而HashMap這幾個方法全部重寫了,是以不會影響使用
     */
    transient volatile Set<K> keySet = null;
    transient volatile Collection<V> values = null;

    private static class Holder {

        /**
         * 備選hash門檻值
         */
        static final int ALTERNATIVE_HASHING_THRESHOLD;

        static {
            // 從系統擷取門檻值的配置,一般沒有配置
            String altThreshold = java.security.AccessController.doPrivileged(
                    new sun.security.action.GetPropertyAction(
                            "jdk.map.althashing.threshold"));

            int threshold;
            try {
                // 給門檻值指派
                threshold = (null != altThreshold)
                        ? Integer.parseInt(altThreshold)
                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;

                // 對門檻值合法性進行校驗
                if (threshold == -1) {
                    threshold = Integer.MAX_VALUE;
                }

                if (threshold < 0) {
                    throw new IllegalArgumentException("value must be positive integer.");
                }
            } catch (IllegalArgumentException failed) {
                throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
            }

            // 最終給備選門檻值設定值
            ALTERNATIVE_HASHING_THRESHOLD = threshold;
        }
    }

    /**
     * hash種子
     */
    transient int hashSeed = 0;

    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);
        // 初始化負載因子和門檻值
        // 注意:門檻值在這裡并非穩定值,在put方法進行初始化後,會等于數組長度*負載因子的值
        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        // init是空實作,忽略
        init();
    }

    // 接收容量的構造器,并将負載因子設定為預設的0.75
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    // 用得最多的構造器,預設容量16、負載因子0.75
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    // 接收一個map的構造器
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    }

    // 根據傳入的值,得到一個接近的2的n次方數
    private static int roundUpToPowerOf2(int number) {
        // 以下操作分三步
        // 1、判斷是否超過容量最大值,超過則取容量最大值
        // 2、判斷是否小于最小值,小于則取最小值1
        // 3、當上述情況不符合,則說明值在正常範圍
        // 讓該值-1再乘以2,然後調用Integer的highestOneBit方法,向下取一個最近的2的n次方值
        // 如:1取1(2^0)、 2-3取2(2^1)、 4-7取4(2^2) 、8-15取8(2^3) 、16-31取16(2^4)
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
    }

    // 初始化table的值
    private void inflateTable(int toSize) {
        // 将容量控制為2的n次方
        int capacity = roundUpToPowerOf2(toSize);

        // 門檻值載不大于MAXIMUM_CAPACITY+1的情況下,值等于capacity*loadFactor
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        // 根據容量,建立Entry數組
        table = new Entry[capacity];
        // 初始化hash種子,可能會對hashSeed字段指派,hashSeed可能會影響hash(Object key)方法的計算,下面會講述
        initHashSeedAsNeeded(capacity);
    }

    void init() {
    }

    // 初始化哈希掩碼值。我們将初始化推遲到真正需要的時候。
    final boolean initHashSeedAsNeeded(int capacity) {
        // 第一次進入該方法,currentAltHashing一直為false
        boolean currentAltHashing = hashSeed != 0;
        // 隻有等到容量值超過2147483647,則會觸發給hashSeed指派
        // sun.misc.VM.isBooted為虛拟機啟動後一直為true && 容量大于最大值
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;

        if (switching) {
            // 假如useAltHashing為true,則将hashSeed設定為随機産生的HashSeed
            hashSeed = useAltHashing
                    ? sun.misc.Hashing.randomHashSeed(this)
                    : 0;
        }
        return switching;
    }

    // 對key值作hash運算
    final int hash(Object k) {
        // hashSeed在大部分情況下是0,當容量超過2147483647後,可能會變為一個随機hashSeed(該情況不做分析)
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        // h為0的時候,下面異或操作結果将是 右邊操作數的值,則下面相當于h = k.hashCode()
        h ^= k.hashCode();

        // 以下代碼使用了擾動計算,異或了高位+低位的值,避免高位沒有參與計算,導緻在與table-1做與運算的時候,無法散列在各個下标值下
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }


    // 擷取目前key下落在table哪個下标,用與運算,等效取模,但性能比取模快
    static int indexFor(int h, int length) {
        // 首先,length要求為2的n次方,2的n次方-1的時候,得到一個二進制是全1的值
        // 當h的值去與操作一個全1的值,等效于對 %length
        // 那為什麼不用取模操作,而要與操作呢?因為與操作效率比取模操作速度快很多
        return h & (length - 1);
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    // 取值
    public V get(Object key) {
        // 由于HashMap支援一個null值,因為key為null的時候,作特殊處理
        // null的值固定在table[0]下
        if (key == null)
            return getForNullKey();
        // 取entry
        Entry<K, V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }

    // 取key=null的值
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    // 擷取鍵值對
    final Entry<K, V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        // 計算key的hash值
        int hash = (key == null) ? 0 : hash(key);
        // 周遊某個下标下的連結清單(如果是連結清單的話)
        for (Entry<K, V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            // 如果key相同且值相同,則傳回
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

    // 添加值
    public V put(K key, V value) {
        // 如果table為空,則根據門檻值初始化table的值。
        // 由于HashMap是延遲初始化的,構造器隻給table指派為EMPTY_TABLE
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        // 特殊處理key=null的情況
        if (key == null)
            return putForNullKey(value);
        // 計算hash值
        int hash = hash(key);
        // 根據hash值與table長度,取模(實際為與操作)得到下标值
        int i = indexFor(hash, table.length);
        // 判斷在目前下标下的連結清單有沒有相同key值的Entry,有則覆寫,并将舊值傳回
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        // 假如沒有相同key值,則将為HashMap增加一個新值
        // 由于結構發生改變,是以modCount需要+1
        modCount++;
        // 添加新的Entry值
        addEntry(hash, key, value, i);
        return null;
    }

    // 特殊處理key=null的值,将其放在table[0]下
    private V putForNullKey(V value) {
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }

    private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : hash(key);
        int i = indexFor(hash, table.length);

        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                e.value = value;
                return;
            }
        }

        createEntry(hash, key, value, i);
    }

    private void putAllForCreate(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            putForCreate(e.getKey(), e.getValue());
    }

    // 擴容方法,多線程不安全,其他方法也是多線程不安全,但是resize問題比較嚴重
    // 可能形成環形連結清單,造成死循環,CPU100%
    void resize(int newCapacity) {
        // 暫存舊的table
        Entry[] oldTable = table;
        // 暫存舊table長度
        int oldCapacity = oldTable.length;
        // 假如舊的table長度等于最大容量,将不做擴容操作
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        // 根據新容量,建立新table
        Entry[] newTable = new Entry[newCapacity];
        // 執行table上面值得轉移,将oldTable上面的值轉移到newTable,會重新hash并取模來判斷新的下标值
        // 多線程環境下,下面方法可能會産生環形連結清單,造成死循環
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        // 将newTable 替換為table
        table = newTable;
        // 重新計算門檻值 ,控制門檻值不能超過MAXIMUM_CAPACITY+1
        threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

    // 轉移值
    void transfer(Entry[] newTable, boolean rehash) {
        // 擷取新的容量
        int newCapacity = newTable.length;
        // 周遊table的Entry
        for (Entry<K, V> e : table) {
            // 周遊連結清單(如果是連結清單的話)
            while (null != e) {
                // 假如線程A取到next,然後時間片用完,CPU切換到線程B繼續執行連結清單值轉移操作,并執行完畢
                // 那麼線程A有了CPU時間片後,繼續執行,此時再觸發轉移值操作,很容易會形成環形連結清單,造成死循環
                Entry<K, V> next = e.next;

                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                // 根據hash值取模,得到在新table的下标值
                int i = indexFor(e.hash, newCapacity);
                // 采用頭插法,将目前節點下一個節點指向newTable目前下标的值
                e.next = newTable[i];
                // 将目前節點作為newTable的頭結點
                newTable[i] = e;
                // 将下一個節點指派給下一輪循環的結點,繼續往下執行循環
                e = next;
            }
        }
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;

        if (table == EMPTY_TABLE) {
            inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
        }

        if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int) (numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }

        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    // 移除key方法
    public V remove(Object key) {
        // 移除key對應的Entry
        Entry<K, V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    // 移除key對應的Entry
    final Entry<K, V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        // 根據hash值和table長度取模,計算目前key所在table的下标
        int hash = (key == null) ? 0 : hash(key);
        int i = indexFor(hash, table.length);
        // 擷取下标所在頭結點
        Entry<K, V> prev = table[i];
        Entry<K, V> e = prev;

        // 周遊連結清單
        while (e != null) {
            Entry<K, V> next = e.next;
            Object k;
            // 假如hash值相等 && key的equals方法也相同,則将執行移除
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                // 結構變化自增
                modCount++;
                // 大小減1
                size--;
                // 判斷目前key是否為頭結點,如果是,需要将next做為新的頭結點,放在table[i]下
                // 為什麼說prev==e就是key為頭結點呢,因為prev==e隻有在第一次判斷的時候才會相等,下面的prev=e和e=next會改變其值了
                if (prev == e)
                    table[i] = next;
                    // 目前key對應的值非頭結點,則将目前結點的next指派給上個結點的next,因為準備删除目前結點
                else
                    prev.next = next;
                // 空方法,忽略
                e.recordRemoval(this);
                return e;
            }
            // 給下一輪循環的結點指派,向下周遊結點
            prev = e;
            e = next;
        }

        return e;
    }

    final Entry<K, V> removeMapping(Object o) {
        if (size == 0 || !(o instanceof Map.Entry))
            return null;

        Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
        Object key = entry.getKey();
        int hash = (key == null) ? 0 : hash(key);
        int i = indexFor(hash, table.length);
        Entry<K, V> prev = table[i];
        Entry<K, V> e = prev;

        while (e != null) {
            Entry<K, V> next = e.next;
            if (e.hash == hash && e.equals(entry)) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }

    public void clear() {
        modCount++;
        Arrays.fill(table, null);
        size = 0;
    }

    public boolean containsValue(Object value) {
        if (value == null)
            return containsNullValue();

        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            for (Entry e = tab[i]; e != null; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }

    private boolean containsNullValue() {
        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            for (Entry e = tab[i]; e != null; e = e.next)
                if (e.value == null)
                    return true;
        return false;
    }

    public Object clone() {
        HashMap<K, V> result = null;
        try {
            result = (HashMap<K, V>) super.clone();
        } catch (CloneNotSupportedException e) {
            // assert false;
        }
        if (result.table != EMPTY_TABLE) {
            result.inflateTable(Math.min(
                    (int) Math.min(
                            size * Math.min(1 / loadFactor, 4.0f),
                            // we have limits...
                            HashMap.MAXIMUM_CAPACITY),
                    table.length));
        }
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);

        return result;
    }

    // 鍵值對,其在維護key和value的同時,還會維護next(下一個Entry)和hash(key的hash值)
    static class Entry<K, V> implements Map.Entry<K, V> {
        final K key;
        V value;
        Entry<K, V> next;
        int hash;

        Entry(int h, K k, V v, Entry<K, V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry) o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        void recordAccess(HashMap<K, V> m) {
        }

        void recordRemoval(HashMap<K, V> m) {
        }
    }

    // 添加新的鍵值對
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // 判斷目前容器大小是否大于等于門檻值 && 目前table下标的值存在值,将進行擴容操作
        if ((size >= threshold) && (null != table[bucketIndex])) {
            // 擴容為目前table長度的兩倍
            resize(2 * table.length);
            // 重新計算擴容後,目前key将位于table的哪個下标
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        // 建立鍵值對
        createEntry(hash, key, value, bucketIndex);
    }
    // 建立鍵值對
    void createEntry(int hash, K key, V value, int bucketIndex) {
        // 擷取指定下标的元素,作為新結點的next,然後将新結點作為table[index]的頭結點
        Entry<K, V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        // 大小+1
        size++;
    }
    // Hash疊代器,KeyIterator和ValueIterator的父類
    private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K, V> next;        // 需要傳回的下一個Entry
        int expectedModCount;    // 快速失敗的判斷依據,如果該值不等于HashMap的modCount,将抛出ConcurrentModificationException
        int index;               // 目前疊代下标
        Entry<K, V> current;     // 目前Entry

        HashIterator() {
            // 将HashMap結構修改次數儲存一個副本,下面疊代的時候,如果兩個值不相等
            // 将抛出并發修改異常(其實不太準确,下面案例分析)ConcurrentModificationException
            expectedModCount = modCount;
            if (size > 0) {
                // 周遊table,直到定位到table不為null的值
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null) ;
            }
        }

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

        final Entry<K, V> nextEntry() {
            // 判斷在執行疊代器期間,HashMap有沒有對結構進行修改
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Entry<K, V> e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
            current = e;
            return e;
        }

        public void remove() {
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }

    private final class ValueIterator extends HashIterator<V> {
        public V next() {
            return nextEntry().value;
        }
    }

    private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }

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

    Iterator<K> newKeyIterator() {
        return new KeyIterator();
    }

    Iterator<V> newValueIterator() {
        return new ValueIterator();
    }

    Iterator<Map.Entry<K, V>> newEntryIterator() {
        return new EntryIterator();
    }

    // 鍵值對集合
    private transient Set<Map.Entry<K, V>> entrySet = null;

    // 擷取key集合
    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }
    // key集合類,特點:其成員本身不儲存元素值,而是在每次疊代的時候,去取HashMap的table上的值
    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }

        public int size() {
            return size;
        }

        public boolean contains(Object o) {
            return containsKey(o);
        }

        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }

        public void clear() {
            HashMap.this.clear();
        }
    }

    // 擷取值得集合
    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    // value集合類,特點:其成員本身不儲存元素值,而是在每次疊代的時候,去取HashMap的table上的值
    private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return newValueIterator();
        }

        public int size() {
            return size;
        }

        public boolean contains(Object o) {
            return containsValue(o);
        }

        public void clear() {
            HashMap.this.clear();
        }
    }

    public Set<Map.Entry<K, V>> entrySet() {
        return entrySet0();
    }

    private Set<Map.Entry<K, V>> entrySet0() {
        Set<Map.Entry<K, V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

    // 鍵值對集合類
    private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
        public Iterator<Map.Entry<K, V>> iterator() {
            return newEntryIterator();
        }

        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K, V> e = (Map.Entry<K, V>) o;
            Entry<K, V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }

        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }

        public int size() {
            return size;
        }

        public void clear() {
            HashMap.this.clear();
        }
    }

    // 序列化
    private void writeObject(java.io.ObjectOutputStream s)
            throws IOException {
        // Write out the threshold, loadfactor, and any hidden stuff
        s.defaultWriteObject();

        // Write out number of buckets
        if (table == EMPTY_TABLE) {
            s.writeInt(roundUpToPowerOf2(threshold));
        } else {
            s.writeInt(table.length);
        }

        // Write out size (number of Mappings)
        s.writeInt(size);

        // Write out keys and values (alternating)
        if (size > 0) {
            for (Map.Entry<K, V> e : entrySet0()) {
                s.writeObject(e.getKey());
                s.writeObject(e.getValue());
            }
        }
    }

    private static final long serialVersionUID = 362498820763181265L;

    // 反序列化
    private void readObject(java.io.ObjectInputStream s)
            throws IOException, ClassNotFoundException {
        // Read in the threshold (ignored), loadfactor, and any hidden stuff
        s.defaultReadObject();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                    loadFactor);
        }

        // set other fields that need values
        table = (Entry<K, V>[]) EMPTY_TABLE;

        // Read in number of buckets
        s.readInt(); // ignored.

        // Read number of mappings
        int mappings = s.readInt();
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " +
                    mappings);

        // capacity chosen by number of mappings and desired load (if >= 0.25)
        int capacity = (int) Math.min(
                mappings * Math.min(1 / loadFactor, 4.0f),
                // we have limits...
                HashMap.MAXIMUM_CAPACITY);

        // allocate the bucket array;
        if (mappings > 0) {
            inflateTable(capacity);
        } else {
            threshold = capacity;
        }

        init();  // Give subclass a chance to do its thing.

        // Read the keys and values, and put the mappings in the hashmap1_7.HashMap
        for (int i = 0; i < mappings; i++) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            putForCreate(key, value);
        }
    }

    // 傳回容量
    int capacity() {
        return table.length;
    }

    // 傳回負載因子
    float loadFactor() {
        return loadFactor;
    }
}

           

https://gitee.com/lao-dubbo/source-code