Java集合之HashMap

轉載請聲明出處:http://blog.csdn.net/qq_24692041/article/details/65438687        


我們先說說HashMap的幾個特點,然後再來慢慢分析!

  1.      HashMap是基於Hash 表實現的。Hash表其實是一個Hash數組+多個單鏈表組成。HashMap會將Key算出hash值,然後每一個hash值對應一個單鏈表,單鏈表中的每一個節點就用於我們存入的數據。然後將這些單鏈表的頭結點存放在Hash數組中。

    這就是hash表的結構,紫色的是hash數組,就是源碼中的table。綠色的是鏈表,就是HashMapEntry節點連接起來的。數據的存儲方式就是將=計算key值的hash值,然後將hash值相等的數據存放在同一個鏈表中(注意:hash值相等不代表key值相等,具體的可以去看看hash值的算法)。對於鏈表認識的不夠的小夥伴可以去《數據結構之Java單鏈表反轉》一文中看看,保證讓你看懂!
  2.      HashMap通過實現Map接口,提過所有對map的操作。
  3.      HashMap允許key值和value值爲null,並且將爲null的key存放在hash數組的第1個(下標爲0)也即是table[0]的鏈表中。
  4.      HashMap不保證操作(根據插入的順序取出)的順序,因爲在插入的時候內部是通過計算key值的hash值,將hash值相同的數據存放在相同的鏈表中,並且是通過hash值來排序的。並不是跟鏈表一樣,保證插入的順序。
  5.      HashMap性能方面主要考慮capacity容量和loadFactor加載因子,默認容量capacity爲4,默認加載因子loadFactor爲 0.75。如果沒有手動設置這兩個值就會用默認值。capacity表示hash數組也就是table的長度,當容量不夠的時候通過調用resize方法擴容。loadFactor加載因子代表數據的散列情況,值越大散列的越好,但是遍歷速度越慢,因爲鏈表的長度變長了。
  6.   HashMap實現了Serializable接口,因此它支持序列化,實現了Cloneable接口,能被克隆
  7. HashMap除了key值和value值允許爲空,不支持線程安全外跟HashTable基本等同。
  8. hash數組的容量一定是2的偶數次冪,因爲hashMap是將數據根據key值算出hash值,創建鏈表,然後散列在table表中。所以計算出來的這個索引必須可以是奇數也可是偶數,才能完全充分的利用內存,否則內存只能使用一半。而計算索引的indexFor方法中的算法是固定的,必須保證table數組的length是2的偶數次冪才能保證這一點。                 

  下面我們摘取一部分源代碼,具體的來看看,搞清楚HashMap內部的結構和具體的原理,建議網友在看源代碼的時候以構造函數,put,get方法爲入口順藤摸瓜,一點點看下去就簡單了:

   

public class HashMap<K, V> extends AbstractMap<K, V>
        implements Map<K, V>, Cloneable, Serializable {

    /**
     * map的默認容量
     */
    static final int DEFAULT_INITIAL_CAPACITY = 4;

    /**
     * 最大容量,2的30次方,當存入過大時,將使用這個容量,並且保證是2的偶數次方
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 加載因子默認值,代表着map的散列情況
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

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

    /**
     * hash數組用於存儲key值計算出來的hash值對應的鏈表的頭指針,容量必須是2的偶數冪
     */
    transient HashMapEntry<K, V>[] table = (HashMapEntry<K, V>[]) EMPTY_TABLE;

    /**
     * 已經使用的容量槽(已經存放的數據的量)
     */
    transient int size;

    /**
     * HashMap的閾值,用於判斷是否需要增加容量
     */
    // If table == EMPTY_TABLE then this is the initial capacity at which the
    // table will be created when inflated.
    int threshold;

    /**
     * hash表的實際加載因子
     */
// Android-Note: We always use a load factor of 0.75 and ignore any explicitly
// selected values.
    final float loadFactor = DEFAULT_LOAD_FACTOR;

    /**
     * HashMap被改變的次數(包括已經存放的值的數量的改變和hashMap內部構造的改變)
     */
    transient int modCount;

    /**
     * 構造方法,指定容量和加載因子
     */
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY) {
            initialCapacity = MAXIMUM_CAPACITY;
        } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
            initialCapacity = DEFAULT_INITIAL_CAPACITY;
        }

        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        // Android-Note: We always use the default load factor of 0.75f.

        // This might appear wrong but it's just awkward design. We always call
        // inflateTable() when table == EMPTY_TABLE. That method will take "threshold"
        // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with
        // the load factor).
        threshold = initialCapacity;
        init();
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    /**
     * 傳入一個Map集合,會創建一個新的Map集合,並且根據傳進來的Map集合的大小對新的Map擴容,然後將數據全部給新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);
    }

    /**
     * 處理擴充hash數組的大小數據,保證容量是2的偶次冪
     */
    private static int roundUpToPowerOf2(int number) {
        // assert number >= 0 : "number must be non-negative";
        int rounded = number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (rounded = Integer.highestOneBit(number)) != 0
                ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded
                : 1;

        return rounded;
    }

    /**
     * hash數組table擴容
     */
    private void inflateTable(int toSize) {
        // Find a power of 2 >= toSize
        int capacity = roundUpToPowerOf2(toSize);

        // Android-changed: Replace usage of Math.min() here because this method is
        // called from the <clinit> of runtime, at which point the native libraries
        // needed by Float.* might not be loaded.
        //閾值計算,等於hash數組的容量乘以加載因子
        float thresholdFloat = capacity * loadFactor;
        if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
            thresholdFloat = MAXIMUM_CAPACITY + 1;
        }

        threshold = (int) thresholdFloat;
        //因爲table還是空數組,所以初始化table
        table = new HashMapEntry[capacity];
    }

    // internal utilities

    /**
     * Initialization hook for subclasses. This method is called
     * in all constructors and pseudo-constructors (clone, readObject)
     * after HashMap has been initialized but before any entries have
     * been inserted.  (In the absence of this method, readObject would
     * require explicit knowledge of subclasses.)
     */
    void init() {
    }

    /**
     * Returns index for hash code h.
     * 返回傳入的hash值在table數組中的索引
     */
    static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        return h & (length - 1);
    }

    /**
     * Returns the number of key-value mappings in this map.
     *
     * @return the number of key-value mappings in this map
     */
    public int size() {
        return size;
    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     *
     * @return <tt>true</tt> if this map contains no key-value mappings
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 獲取key所對應的value     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        //如果keynull,遍歷集合取出key值爲nullvalue        if (key == null)
            return getForNullKey();
        //如果Key值不爲null,獲取這個key所在的節點
        Entry<K, V> entry = getEntry(key);
        //通過節點拿到value值返回
        return null == entry ? null : entry.getValue();
    }

    /**
     * key值爲null的數據存放在第1個鏈表也即是table[0]的鏈表,
     * table中取出鏈表遍歷獲取key值爲nullvalue     */
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (HashMapEntry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

    /**
     * 查找key值所在的節點,判斷集合中是否包含該數據
     *
     * @param key The key whose presence in this map is to be tested
     * @return <tt>true</tt> if this map contains a mapping for the specified
     * key.
     */
    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    /**
     * 如果key值爲nullhash直接爲0,存入到table[0]中存放的鏈表。
     * 如果不爲0計算key值的hash值,並找到該hash值所在的鏈表,從這個鏈表中找到key所在的節點
     */
    final Entry<K, V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        //計算傳入的key值的hash        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //table中取出在hash值所對應的鏈表,遍歷查找key值所在的節點返回
        for (HashMapEntry<K, V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

    /**
     * map中以鍵值對的形式加入數據
     */
    public V put(K key, V value) {
        //如果Hash數組是空的,就擴容
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key值是空,將該數據存放在table[0]中的鏈表
        if (key == null)
            return putForNullKey(value);
        //如果不爲null,計算key值的hash        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //找到hash值在數組中的索引
        int i = indexFor(hash, table.length);
        //根據索引取出鏈表,遍歷鏈表
        for (HashMapEntry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果在該hash值對應的鏈表中已經存在key的節點,直接替換oldValue,並將oldValue返回,結束該方法
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                //hashMap類中,這個方法什麼也沒做,這個方法是給子類LinkedHashMap用的,用於保證操作排序
                e.recordAccess(this);
                return oldValue;
            }
        }
        /*
         *如果在hash值所在的鏈表中沒有key值對應的節點,就創建新的節點,將key-value放進去
         */
        //增加更改次數
        modCount++;
        //增加當前鏈表的節點,將key-value放進去
        addEntry(hash, key, value, i);
        return null;
    }

    /**
     * 將空key值插入到table[0]鏈表中
     */
    private V putForNullKey(V value) {
        for (HashMapEntry<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;
    }

    /**
     * This method is used instead of put by constructors and
     * pseudoconstructors (clone, readObject).  It does not resize the table,
     * check for comodification, etc.  It calls createEntry rather than
     * addEntry.
     */
    private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        int i = indexFor(hash, table.length);

        /**
         * Look for preexisting entry for key.  This will never happen for
         * clone or deserialize.  It will only happen for construction if the
         * input Map is a sorted map whose ordering is inconsistent w/ equals.
         */
        for (HashMapEntry<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());
    }

    /**
     * 創建出一個更大的table,將舊的table中的數據全部複製到新的table
     */
    void resize(int newCapacity) { 
        HashMapEntry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        //創建如一個新的table
        HashMapEntry[] newTable = new HashMapEntry[newCapacity];
        //複製數據到新的table        transfer(newTable);
        //將新的table賦給整個hashMap中的table
        table = newTable;
        //更改閾值,在默認最大空間+1和新的容量乘上加載因子中取最小值作爲新的閾值
        threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

    /**
     * 將舊table中的鏈表和鏈表中的數據全部複製到新table     */
    void transfer(HashMapEntry[] newTable) {
        int newCapacity = newTable.length;
        //遍歷table數組
        for (HashMapEntry<K, V> e : table) {
            //取出數組中的鏈表,複製到新的table            while (null != e) {
                HashMapEntry<K, V> next = e.next;
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

    /**
     * 將一個map中的所有數據全部複製hashMap     *
     * @param m mappings to be stored in this map
     * @throws NullPointerException if the specified map is null
     */
    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));
        }

        /*
         * Expand the map if the map if the number of mappings to be added
         * is greater than or equal to threshold.  This is conservative; the
         * obvious condition is (m.size() + size) >= threshold, but this
         * condition could result in a map with twice the appropriate capacity,
         * if the keys to be added overlap with the keys already in this map.
         * By using the conservative calculation, we subject ourself
         * to at most one extra resize.
         */
        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) {
        Entry<K, V> e = removeEntryForKey(key);
        return (e == null ? null : e.getValue());
    }

    /**
     * Removes and returns the entry associated with the specified key
     * in the HashMap.  Returns null if the HashMap contains no mapping
     * for this key.
     */
    final Entry<K, V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        int i = indexFor(hash, table.length);
        HashMapEntry<K, V> prev = table[i];
        HashMapEntry<K, V> e = prev;

        while (e != null) {
            HashMapEntry<K, V> next = e.next;
            Object k;
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                //這個方法在HashMap中跟recordAccess一樣,啥都沒做是爲了給自雷linkedHashMap用的
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }

    /**
     * Special version of remove for EntrySet using {@code Map.Entry.equals()}
     * for matching.
     */
    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 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        int i = indexFor(hash, table.length);
        HashMapEntry<K, V> prev = table[i];
        HashMapEntry<K, V> e = prev;

        while (e != null) {
            HashMapEntry<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;
    }

    /**
     * 通過將table中的所有鏈表置null來清空當前HashMap
     */
    public void clear() {
        modCount++;
        Arrays.fill(table, null);
        size = 0;
    }

    /**
     * 遍歷table中所有的鏈表
     * 通過調用Objectequals方法來判斷是否包含該value
     */
    public boolean containsValue(Object value) {
        if (value == null)
            return containsNullValue();

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

    /**
     * 遍歷整個table中所有的鏈表,查找valuenull的數據,只要有一個null就直接返回true
     */
    private boolean containsNullValue() {
        HashMapEntry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            for (HashMapEntry e = tab[i]; e != null; e = e.next)
                if (e.value == null)
                    return true;
        return false;
    }

    /**
     * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
     * values themselves are not cloned.
     *
     * @return a shallow copy of this map
     */
    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;
    }

    /**
     * @hide
     */  // Android added.
    static class HashMapEntry<K, V> implements Map.Entry<K, V> {
        final K key;
        V value;
        HashMapEntry<K, V> next;
        int hash;

        /**
         * Creates new entry.
         */
        HashMapEntry(int h, K k, V v, HashMapEntry<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();
        }

        /**
         * 在put方法中調用,在HashMap中沒有做任何操作,主要是給子類LinkedHashMap重寫,用於保證插入順序
         */
        void recordAccess(HashMap<K, V> m) {
        }

        /**
         * 刪除元素時調用,一樣的在hashMap中沒有做任何操作,爲LinkedHashMap準備
         */
        void recordRemoval(HashMap<K, V> m) {
        }
    }

    /**
     * 在已有的鏈表中爲傳入的key值新增一個節點
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //如果容量槽不夠用,並且當前操作的鏈表不爲null
        if ((size >= threshold) && (null != table[bucketIndex])) {
            //table的容量擴充爲之前的兩倍
             resize(2 * table.length);
            hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
            //爲當前hash值計算出在table中的索引
            bucketIndex = indexFor(hash, table.length);
        }
        //創建新節點
        createEntry(hash, key, value, bucketIndex);
    }

    /**
     *新創建一個節點,並且將該節點作爲當前操作hash值對應鏈表的頭結點
     */
    void createEntry(int hash, K key, V value, int bucketIndex) {  
        //hash值對應的鏈表在table中取出來
 HashMapEntry<K, V> e = table[bucketIndex];
 //創建一個新節點用於存放新的vey-value並且將該節點作爲當前操作鏈表的頭結點
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
 size++;
 }
 ......}

     

      好了,到這兒具體的源代碼差不多夠了。足夠搞清楚HashMap內部的構造和原理。下面我們將其中比較重要的幾個方法拿出來,重點講解一下:


    put方法,用於往hashMap中以key-value的形式添加新的數據, 首先會判斷當前表示否爲空表,然後會判斷當前插入的key值是否爲null,如果爲null,會將該數據插入到表頭,也即是table[0]所在的鏈表。如果不爲null就會算出hash值,取出hash值對應的鏈表,遍歷這個鏈表,如果該鏈表中已經存在這個key值,就會替換舊的value,如果沒有這個key就會創建一個新的節點存放數據,並且會將新創建的節點放在鏈表的頭結點。   

 /**
     * map中以鍵值對的形式加入數據
     */
    public V put(K key, V value) {
        //如果Hash數組是空的,就擴容
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key值是空,將該數據存放在table[0]中的鏈表
        if (key == null)
            return putForNullKey(value);
        //如果不爲null,計算key值的hash        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //找到hash值在數組中的索引
        int i = indexFor(hash, table.length);
        //根據索引取出鏈表,遍歷鏈表
        for (HashMapEntry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果在該hash值對應的鏈表中已經存在key的節點,直接替換oldValue,並將oldValue返回,結束該方法
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                //hashMap類中,這個方法什麼也沒做,這個方法是給子類LinkedHashMap用的,用於保證操作排序
                e.recordAccess(this);
                return oldValue;
            }
        }
        /*
         *如果在hash值所在的鏈表中沒有key值對應的節點,就創建新的節點,將key-value放進去
         */
        //增加更改次數
        modCount++;
        //增加當前鏈表的節點,將key-value放進去
        addEntry(hash, key, value, i);
        return null;
    }

   addEntry方法:這個方法是用於爲鏈表增加節點的,在put方法中,會先去取出hash值對應的鏈表,遍歷,如果已經對應的key值,就更改舊的value值。但是如果沒有這個key值,就要調用該方法增加一個節點存放新插入的數據。

/**
     * 在已有的鏈表中爲傳入的key值新增一個節點
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //如果容量槽不夠用,並且當前操作的鏈表不爲null
        if ((size >= threshold) && (null != table[bucketIndex])) {
            //table的容量擴充爲之前的兩倍
             resize(2 * table.length);
            hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
            //爲當前hash值計算出在table中的索引
            bucketIndex = indexFor(hash, table.length);
        }

        //創建新節點
        createEntry(hash, key, value, bucketIndex);
    }

       createEntry方法:這個方法纔是創建新的節點的具體實現,取出當前hash值對應的鏈表,創建一個新的節點用於存放新插入的數據,並且會將新的節點設置爲當前鏈表的頭結點。

    /**
     *新創建一個節點,並且將該節點作爲當前操作hash值對應鏈表的頭結點
     */
    void createEntry(int hash, K key, V value, int bucketIndex) {
        
        //hash值對應的鏈表在table中取出來
 HashMapEntry<K, V> e = table[bucketIndex];
//創建一個新節點用於存放新的vey-value並且將該節點作爲當前操作鏈表的頭結點
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
  size++;
  }


     get方法:get方法很簡單,就是計算key的hash值,找到這個鏈表,遍歷鏈表取出值。

/**
     * 獲取key所對應的value     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        //如果keynull,遍歷集合取出key值爲nullvalue        if (key == null)
            return getForNullKey();
        //如果Key值不爲null,獲取這個key所在的節點
        Entry<K, V> entry = getEntry(key);
        //通過節點拿到value值返回
        return null == entry ? null : entry.getValue();
    }

     resize方法:這個方法是用於將整個hash表擴容的,會先創建一個新的hash數組,然後將舊的hash數組中的所有鏈表複製到新的hash表中。這個方法是一個數組的複製過程,這是一個耗時的過程,並且在addEntry方法中調用resize方法的時候是將當前容量擴充兩倍的。所以我們在使用的時候最好先約估一下容量的大小。

   /**
     * 創建出一個更大的table,將舊的table中的數據全部複製到新的table
     */
    void resize(int newCapacity) { 
        HashMapEntry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        //創建如一個新的table
        HashMapEntry[] newTable = new HashMapEntry[newCapacity];
        //複製數據到新的table        transfer(newTable);
        //將新的table賦給整個hashMap中的table
        table = newTable;
        //更改閾值,在默認最大空間+1和新的容量乘上加載因子中取最小值作爲新的閾值
        threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

好了,HashMap基本上就這些東西了。沒有更多的需要將的了!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章