天天看點

HashMap的實作原理淺析

HashMap也是我們使用非常多的Collection,它是基于哈希表的 Map 接口的實作,以key-value的形式存在。

HashMap是基于哈希表的Map接口的非同步實作。此實作提供所有可選的映射操作,并允許使用null值和null鍵。此類不保證映射的順序,特别是它不保證該順序恒久不變。

本文使用的是JDK1.7的HashMap源碼,因為JDK1.8的HashMap改變還很多,以後再去單一分析。

HashMap内部結構

檢視HashMap源代碼,可以發現其繼承自AbstractMap, 并實作了Map, Cloneable, Serializable接口

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

複制

HashMap包含一個數組對象table[],

/**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;           

複制

數組中包含的元素為Entry對象,Entry是HashMap中定義的靜态内部類,内容如下:

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;

        /**
         * Creates new entry.
         */
        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();
        }

        /**
         * This method is invoked whenever the value in an entry is
         * overwritten by an invocation of put(k,v) for a key k that's already
         * in the HashMap.
         */
        void recordAccess(HashMap<K,V> m) {
        }

        /**
         * This method is invoked whenever the entry is
         * removed from the table.
         */
        void recordRemoval(HashMap<K,V> m) {
        }
    }           

複制

從Entry的類内容可以看出,其主要包含四個部分,

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;           

複制

key -- 鍵

Value -- 值

next -- 下一個Entry對象

hash -- hash值

從上述的描述可以看出,HashMap的内部結構基本上是通過 “數組” + “連結清單”來實作~ 如下圖所示:

HashMap的實作原理淺析

HashMap預設的大小為16

/**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16           

複制

接下來,使用一個簡單示例來感受一下HashMap的數組+連結清單的結構存儲。如下是一個以名字為鍵,年齡為值的HashMap示例。

import java.util.HashMap;
import java.util.Map;

public class MapExample {

  public static void main(String[] args) {

    Map<String, Integer> nameAgeMap = new HashMap<>();

    nameAgeMap.put("Eric", 20);
    nameAgeMap.put("John", 21);
    nameAgeMap.put("LiLei", 19);
    nameAgeMap.put("Wang", 28);
    nameAgeMap.put("Zhang", 24);

    System.out.println(nameAgeMap);
  }
}           

複制

在輸出結果之前,Debug一下nameAgeMap中的内容,如下圖所示:

HashMap的實作原理淺析

從上述的示例中可以看出,不同的Key根據hash映射到數組(table[])的不同部分上去。

HashMap通過如下方式選擇元素存放在哪一個table[]元素中去:

int hash = hash(key);
        int i = indexFor(hash, table.length);           

複制

其中,

  • hash(Key)的内容為
/**
     * Retrieve object hash code and applies a supplemental hash function to the
     * result hash, which defends against poor quality hash functions.  This is
     * critical because HashMap uses power-of-two length hash tables, that
     * otherwise encounter collisions for hashCodes that do not differ
     * in lower bits. Note: Null keys always map to hash 0, thus index 0.
     */
    final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }           

複制

  • indexFor(hash, table.length)的内容為
/**
     * Returns index for hash code h.
     */
    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);
    }           

複制

HashMap的實作原理淺析

如果不同元素計算出來的hash值是一樣的,HashMap使用連結清單的方式來解決。

就像上述示例中,

table[6]存放了key為“LiLei”的Entry,并且這個Entry的next對象為key為“Eric”的Entry。

對HashMap的資料結構有了大緻了解之後,就可以來看看,HashMap的主要方法實作是怎麼樣的。

HashMap的實作原理淺析

方法put的實作

源代碼

/**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = 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.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }           

複制

put方法步驟

put方法主要包含的部分如下圖所示:

HashMap的實作原理淺析
從源碼可以看出,HashMap是支援鍵為null值的

使用代碼來驗證一下:

import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String[] args) {

        Map<String, Integer> nameAgeMap = new HashMap<>();

        nameAgeMap.put("One", 1);
        nameAgeMap.put("Two", 2);
        nameAgeMap.put("Three", 3);
        nameAgeMap.put(null, 4);

        for (Map.Entry<String, Integer> entry : nameAgeMap.entrySet()) {
            System.out.println(entry.getKey() + ' ' + entry.getValue());
        }

    }

}           

複制

輸出:

null 4
Three 3
One 1
Two 2           

複制

addEntry方法

/**
     * Adds a new entry with the specified key, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     *
     * Subclass overrides this to alter the behavior of put method.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

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

複制

  • addEntry包含兩個部分:
  1. 如果元素越來越多,達到門檻值(threshold),則需要将table大小乘2,并重新hash
  2. 如果未到達門檻值,則添加元素到相應的table[bucketIndex]中

createEntry方法

/**
     * Like addEntry except that this version is used when creating entries
     * as part of Map construction or "pseudo-construction" (cloning,
     * deserialization).  This version needn't worry about resizing the table.
     *
     * Subclass overrides this to alter the behavior of HashMap(Map),
     * clone, and readObject.
     */
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }           

複制

從上述可以看出,table[bucketIndex]中的Entry永遠是存放最新的元素的~

方法get的實作

源代碼

/**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

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

複制

從上述源碼可以看出,HashMap的在實作上考慮了key為null值或者不為null值。

三個步驟==>
  1. 如果key為null值,則調用getForNullKey方法
  2. 如果key不為null值,則調用 getEntry(key)來擷取Entry
  3. 如果擷取的Entry為null,則傳回null;不為null則傳回entry.getValue()

getForNullKey方法

/**
     * Offloaded version of get() to look up null keys.  Null keys map
     * to index 0.  This null case is split out into separate methods
     * for the sake of performance in the two most commonly used
     * operations (get and put), but incorporated with conditionals in
     * others.
     */
    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;
    }           

複制

如果Map中沒有元素,則傳回null,否則從table[0]中查找~

getEntry(Object key)方法

/**
     * Returns the entry associated with the specified key in the
     * HashMap.  Returns null if the HashMap contains no mapping
     * for the key.
     */
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<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;
    }           

複制

  1. 如果size為0,也就是HashMap中沒有元素,則傳回null
  2. 如果HashMap中有元素,則先計算出key值對應的數組元素table[bucketIndex], 然後通過周遊連結清單擷取元素

方法clear的實作

源代碼

/**
     * Removes all of the mappings from this map.
     * The map will be empty after this call returns.
     */
    public void clear() {
        modCount++;
        Arrays.fill(table, null);
        size = 0;
    }           

複制

HashMap中clear方法的實作包含了如下幾個部分

  1. 修改次數加1
  2. 數組table的元素設定成null,調用Arrays.fill方法實作
  3. HashMap中元素的個數size設定成0

Arrays.fill方法如下:

/**
     * Assigns the specified Object reference to each element of the specified
     * array of Objects.
     *
     * @param a the array to be filled
     * @param val the value to be stored in all elements of the array
     * @throws ArrayStoreException if the specified value is not of a
     *         runtime type that can be stored in the specified array
     */
    public static void fill(Object[] a, Object val) {
        for (int i = 0, len = a.length; i < len; i++)
            a[i] = val;
    }           

複制

方法containsKey和containsValue的實作

containsKey的實作

/**
     * Returns <tt>true</tt> if this map contains a mapping for the
     * specified 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;
    }

    /**
     * Returns the entry associated with the specified key in the
     * HashMap.  Returns null if the HashMap contains no mapping
     * for the key.
     */
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<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;
    }           

複制

getEntry(key)方法說明如下:
  1. 如果size為0,也就是HashMap中沒有元素,則傳回null
  2. 如果HashMap中有元素,則先計算出key值對應的數組元素table[bucketIndex], 然後通過周遊連結清單擷取元素

containsValue的實作

/**
     * Returns <tt>true</tt> if this map maps one or more keys to the
     * specified value.
     *
     * @param value value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified value
     */
    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;
    }

    /**
     * Special-case code for containsValue with null argument
     */
    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;
    }           

複制

無論value的值是否為null值,都是采用雙重循環來實作~

方法resize的實作

源代碼

/**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     *
     * If current capacity is MAXIMUM_CAPACITY, this method does not
     * resize the map, but sets threshold to Integer.MAX_VALUE.
     * This has the effect of preventing future calls.
     *
     * @param newCapacity the new capacity, MUST be a power of two;
     *        must be greater than current capacity unless current
     *        capacity is MAXIMUM_CAPACITY (in which case value
     *        is irrelevant).
     */
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }           

複制

就想上面提到過的,如果HashMap中添加元素超過了門檻值,則需要擴容,并重新配置設定元素到新的數組( table[] )中去~

如transfer方法如下所示,其作用就是将所有的元素從目前table數組中,轉移到新的table數組中

transfer方法

/**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }           

複制

HashMap的實作原理淺析
resize方法在添加單個元素的時候,需要考慮,同樣在添加多個元素的時候更要考慮。

putAll方法

/**
     * Copies all of the mappings from the specified map to this map.
     * These mappings will replace any mappings that this map had for
     * any of the keys currently in the specified map.
     *
     * @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());
    }           

複制

putAll方法就是确定好足夠的大小之後,循環調用put方法來實作的~

方法remove的實作

源代碼

/**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key key whose mapping is to be removed from the map
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }           

複制

remove方法其實就是調用了removeEntryForKey方法~

removeEntryForKey如下:

方法removeEntryForKey

/**
     * 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 : 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;
            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;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }           

複制

removeEntryForKey其實就是連結清單的删除的實作,其包含如下幾個部分:
  1. 如果沒有元素,則傳回null值
  2. 然後根據key計算hash值,并确定到哪一個table[bucketIndex]中删除
  3. 循環周遊元素,删除符合條件的Entry節點即可,比較好了解

方法indexFor的實作

源代碼

/**
     * Returns index for hash code h.
     */
    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);
    }           

複制

indexFor方法在HashMap中用于定位數組位置table[bucketIndex]~

int i = indexFor(hash, table.length);

在HashMap中,table的長度定義推薦使用2的N次方~

h & (length - 1) 等價與 h % length,但他們是等價(效果)不等效(效率)的,位運算結果與取模一緻,但效率更高;

這樣做的好處還有:

當數組長度為2的n次幂的時候,不同的key算得得index相同的幾率較小,那麼資料在數組上分布就比較均勻,也就是說碰撞的幾率小,相對的,查詢的時候就不用周遊某個位置上的連結清單,這樣查詢效率也就較高了。 參考【http://www.iteye.com/topic/539465】

modCount是幹嘛的

源代碼

/**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;           

複制

modCount記錄的HashMap結構變化的次數,在疊代器初始化過程中會将這個值賦給疊代器的 expectedModCount,在疊代過程中,判斷 modCount 跟 expectedModCount 是否相等,如果不相等就表示已經有其他線程修改了 Map,那麼将抛出ConcurrentModificationException,這就是所謂fail-fast政策。

可以檢視HashMap中的抽象類HashIterator:

  • 構造函數中expectedModCount 指派
private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K,V> next;        // next entry to return
        int expectedModCount;   // For fast-fail
        int index;              // current slot
        Entry<K,V> current;     // current entry

        HashIterator() {
            expectedModCount = modCount;
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
        }           

複制

  • remove改變抛ConcurrentModificationException異常
final Entry<K,V> nextEntry() {
            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;
        }           

複制

Fail-Fast簡單示例

import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String[] args) {

        Map<String, Integer> nameAgeMap = new HashMap<>();

        nameAgeMap.put("One", 1);
        nameAgeMap.put("Two", 2);
        nameAgeMap.put("Three", 3);
        nameAgeMap.put(null, 4);
        nameAgeMap.put("five", null);

        for (Map.Entry<String, Integer> entry : nameAgeMap.entrySet()) {
            if(null == entry.getKey()) {
                nameAgeMap.remove(null);
            }
            System.out.println(entry.getKey() + ' ' + entry.getValue());
        }

    }

}           

複制

運作結果:

null 4
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
	at java.util.HashMap$EntryIterator.next(Unknown Source)
	at java.util.HashMap$EntryIterator.next(Unknown Source)
	at Test.main(Test.java:16)           

複制

這裡就不對HashMap中的方法一一列舉了~

JDK 1.8 HashMap調整

HashMap在JDK1.8中,相比JDK 1.7做了較多的改動。

HashMap的資料結構(數組+連結清單+紅黑樹),桶中的結構可能是連結清單,也可能是紅黑樹,可以提高效率。

如put方法~

/**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }           

複制

putVal方法

/**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                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;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }           

複制

其中, treeifyBin(tab, hash)部分就是紅黑樹的相關處理部分~

參考

  • http://coding-geek.com/how-does-a-hashmap-work-in-java/
  • http://javaconceptoftheday.com/how-hashmap-works-internally-in-java