Java 集合框架源码分析(三)——HashMap 转载BridgeGeorge Java 集合框架源码分析(三)——HashMap



介绍

HashMap 是Java 集合框架中重要的组成部分,也是平常使用频率很高的一个集合类,典型使用方式如下:

<code class="hljs r has-numbering">Map<Integer,String> map=new HashMap<>();
map.put(<span class="hljs-number">1</span>,<span class="hljs-string">"Java EE"</span>);
map.put(<span class="hljs-number">2</span>,<span class="hljs-string">"Android"</span>);
<span class="hljs-keyword">...</span></code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

它的类继承层级结构如下。

类层次

  java.lang.Object
      继承者 java.util.AbstractMap< K,V >
          继承者 java.util.HashMap< K,V >

HashMap是基于哈希表实现的,每一个元素都是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阈值)时,同样会自动增长。
HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap
HashMap实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。

HashMap源码剖析

以下代码基于SUN JDK 1.7(注意,不同的JDK的实现细节可能不太一样,我们只关注原理,不去太追求细节)加入了比较详细的注释。

<code class="hljs cs has-numbering">package java.util;
import java.io.*;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{

   <span class="hljs-comment">// 默认的初始容量(容量为HashMap中槽的数目)是16,且实际容量必须是2的整数次幂。 </span>
    <span class="hljs-keyword">static</span> final <span class="hljs-keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="hljs-number">1</span> << <span class="hljs-number">4</span>; <span class="hljs-comment">// aka 16</span>

   <span class="hljs-comment">// 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)   </span>
    <span class="hljs-keyword">static</span> final <span class="hljs-keyword">int</span> MAXIMUM_CAPACITY = <span class="hljs-number">1</span> << <span class="hljs-number">30</span>;

    <span class="hljs-comment">// 默认加载因子为0.75 </span>
    <span class="hljs-keyword">static</span> final <span class="hljs-keyword">float</span> DEFAULT_LOAD_FACTOR = <span class="hljs-number">0.75</span>f;


    <span class="hljs-comment">//当table未被填充时空的table实例</span>
    <span class="hljs-keyword">static</span> final Entry<?,?>[] EMPTY_TABLE = {};


<span class="hljs-comment">//resize操作需要用这个table,且长度必须是2的n次方。</span>
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

   / HashMap的底层数组中已用槽的数量
    transient <span class="hljs-keyword">int</span> size;

   <span class="hljs-comment">// HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)</span>
    <span class="hljs-keyword">int</span> threshold;

    <span class="hljs-comment">// 加载因子实际大小 </span>
    final <span class="hljs-keyword">float</span> loadFactor;

    <span class="hljs-comment">// HashMap被改变的次数</span>
    transient <span class="hljs-keyword">int</span> modCount;


<span class="hljs-comment">//备用的Hash临界值,降低因弱Hash计算导致的hash冲突,可以通过系统变量重写这个值</span>
    <span class="hljs-keyword">static</span> final <span class="hljs-keyword">int</span> ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;


     <span class="hljs-comment">//虚拟机启动后,holder中的values才能被初始化,保证值的重写生效</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> Holder {

        <span class="hljs-comment">/**
         * Table capacity above which to switch to use alternative hashing.
         */</span>
        <span class="hljs-keyword">static</span> final <span class="hljs-keyword">int</span> ALTERNATIVE_HASHING_THRESHOLD;

        <span class="hljs-keyword">static</span> {
            String altThreshold = java.security.AccessController.doPrivileged(
                <span class="hljs-keyword">new</span> sun.security.action.GetPropertyAction(
                    <span class="hljs-string">"jdk.map.althashing.threshold"</span>));

            <span class="hljs-keyword">int</span> threshold;
            <span class="hljs-keyword">try</span> {
                threshold = (<span class="hljs-keyword">null</span> != altThreshold)
                        ? Integer.parseInt(altThreshold)
                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;

                <span class="hljs-comment">// disable alternative hashing if -1</span>
                <span class="hljs-keyword">if</span> (threshold == -<span class="hljs-number">1</span>) {
                    threshold = Integer.MAX_VALUE;
                }

                <span class="hljs-keyword">if</span> (threshold < <span class="hljs-number">0</span>) {
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"value must be positive integer."</span>);
                }
            } <span class="hljs-keyword">catch</span>(IllegalArgumentException failed) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error(<span class="hljs-string">"Illegal value for 'jdk.map.althashing.threshold'"</span>, failed);
            }

            ALTERNATIVE_HASHING_THRESHOLD = threshold;
        }
    }


     <span class="hljs-comment">//随机值hashSeed减少hash碰撞,如果为0,备选hash被禁用</span>
    transient <span class="hljs-keyword">int</span> hashSeed = <span class="hljs-number">0</span>;

     <span class="hljs-comment">// 指定“容量大小”和“加载因子”的构造函数</span>
    <span class="hljs-keyword">public</span> <span class="hljs-title">HashMap</span>(<span class="hljs-keyword">int</span> initialCapacity, <span class="hljs-keyword">float</span> loadFactor) {
        <span class="hljs-keyword">if</span> (initialCapacity < <span class="hljs-number">0</span>)
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Illegal initial capacity: "</span> +
                                               initialCapacity);
        <span class="hljs-keyword">if</span> (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        <span class="hljs-keyword">if</span> (loadFactor <= <span class="hljs-number">0</span> || Float.isNaN(loadFactor))
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Illegal load factor: "</span> +
                                               loadFactor);

        <span class="hljs-keyword">this</span>.loadFactor = loadFactor;
        threshold = initialCapacity;
        init();
    }

   <span class="hljs-comment">// 指定“容量大小”的构造函数  </span>
    <span class="hljs-keyword">public</span> <span class="hljs-title">HashMap</span>(<span class="hljs-keyword">int</span> initialCapacity) {
        <span class="hljs-keyword">this</span>(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    <span class="hljs-comment">// 默认构造函数。 </span>
    <span class="hljs-keyword">public</span> <span class="hljs-title">HashMap</span>() {
        <span class="hljs-keyword">this</span>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

   <span class="hljs-comment">//包含“子Map”的构造函数</span>
    <span class="hljs-keyword">public</span> <span class="hljs-title">HashMap</span>(Map<? extends K, ? extends V> m) {
        <span class="hljs-keyword">this</span>(Math.max((<span class="hljs-keyword">int</span>) (m.size() / DEFAULT_LOAD_FACTOR) + <span class="hljs-number">1</span>,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">roundUpToPowerOf2</span>(<span class="hljs-keyword">int</span> number) {
        <span class="hljs-comment">// assert number >= 0 : "number must be non-negative";</span>
        <span class="hljs-keyword">return</span> number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > <span class="hljs-number">1</span>) ? Integer.highestOneBit((number - <span class="hljs-number">1</span>) << <span class="hljs-number">1</span>) : <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">/**
     * Inflates the table.
     */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">inflateTable</span>(<span class="hljs-keyword">int</span> toSize) {
        <span class="hljs-comment">// Find a power of 2 >= toSize</span>
        <span class="hljs-keyword">int</span> capacity = roundUpToPowerOf2(toSize);

        threshold = (<span class="hljs-keyword">int</span>) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + <span class="hljs-number">1</span>);
        table = <span class="hljs-keyword">new</span> Entry[capacity];
        initHashSeedAsNeeded(capacity);
    }

    <span class="hljs-comment">// internal utilities</span>

    <span class="hljs-comment">/**
     * 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.)
     */</span>
    <span class="hljs-keyword">void</span> init() {
    }

    <span class="hljs-comment">/**
     * Initialize the hashing mask value. We defer initialization until we
     * really need it.
     */</span>
    final boolean initHashSeedAsNeeded(<span class="hljs-keyword">int</span> capacity) {
        boolean currentAltHashing = hashSeed != <span class="hljs-number">0</span>;
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;
        <span class="hljs-keyword">if</span> (switching) {
            hashSeed = useAltHashing
                ? sun.misc.Hashing.randomHashSeed(<span class="hljs-keyword">this</span>)
                : <span class="hljs-number">0</span>;
        }
        <span class="hljs-keyword">return</span> switching;
    }

    <span class="hljs-comment">//求hash值的方法,重新计算hash值</span>
    final <span class="hljs-keyword">int</span> hash(Object k) {
        <span class="hljs-keyword">int</span> h = hashSeed;
        <span class="hljs-keyword">if</span> (<span class="hljs-number">0</span> != h && k instanceof String) {
            <span class="hljs-keyword">return</span> sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

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

   <span class="hljs-comment">// 返回h在数组中的索引值,这里用&代替取模,旨在提升效率   </span>
    <span class="hljs-comment">// h & (length-1)保证返回值的小于length  </span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> indexFor(<span class="hljs-keyword">int</span> h, <span class="hljs-keyword">int</span> length) {
        <span class="hljs-comment">// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";</span>
        <span class="hljs-keyword">return</span> h & (length-<span class="hljs-number">1</span>);
    }


    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">size</span>() {
        <span class="hljs-keyword">return</span> size;
    }

    <span class="hljs-comment">//返回HashMap是否为空</span>
    <span class="hljs-keyword">public</span> boolean <span class="hljs-title">isEmpty</span>() {
        <span class="hljs-keyword">return</span> size == <span class="hljs-number">0</span>;
    }

    <span class="hljs-comment">// 获取key对应的value </span>
    <span class="hljs-keyword">public</span> V <span class="hljs-title">get</span>(Object key) {
        <span class="hljs-keyword">if</span> (key == <span class="hljs-keyword">null</span>)
            <span class="hljs-keyword">return</span> getForNullKey();
        Entry<K,V> entry = getEntry(key);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span> == entry ? <span class="hljs-keyword">null</span> : entry.getValue();
    }

   <span class="hljs-comment"><span class="hljs-xmlDocTag">///</span>/ 获取“key为null”的元素的值    </span>
    <span class="hljs-comment">// HashMap将“key为null”的元素存储在table[0]位置,但不一定是该链表的第一个位置!</span>
    <span class="hljs-keyword">private</span> V <span class="hljs-title">getForNullKey</span>() {
        <span class="hljs-keyword">if</span> (size == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[<span class="hljs-number">0</span>]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            <span class="hljs-keyword">if</span> (e.key == <span class="hljs-keyword">null</span>)
                <span class="hljs-keyword">return</span> e.<span class="hljs-keyword">value</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-comment">// HashMap是否包含key</span>
    <span class="hljs-keyword">public</span> boolean <span class="hljs-title">containsKey</span>(Object key) {
        <span class="hljs-keyword">return</span> getEntry(key) != <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-comment">// 返回“键为key”的键值对</span>
    final Entry<K,V> getEntry(Object key) {
        <span class="hljs-keyword">if</span> (size == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }

        <span class="hljs-keyword">int</span> hash = (key == <span class="hljs-keyword">null</span>) ? <span class="hljs-number">0</span> : hash(key);
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != <span class="hljs-keyword">null</span>;
             e = e.next) {
            Object k;
            <span class="hljs-keyword">if</span> (e.hash == hash &&
                ((k = e.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k))))
                <span class="hljs-keyword">return</span> e;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

    <span class="hljs-comment">// 将“key-value”添加到HashMap中 </span>
    <span class="hljs-keyword">public</span> V <span class="hljs-title">put</span>(K key, V <span class="hljs-keyword">value</span>) {
        <span class="hljs-keyword">if</span> (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        <span class="hljs-keyword">if</span> (key == <span class="hljs-keyword">null</span>)
            <span class="hljs-keyword">return</span> putForNullKey(<span class="hljs-keyword">value</span>);
        <span class="hljs-keyword">int</span> hash = hash(key);
        <span class="hljs-keyword">int</span> i = indexFor(hash, table.length);
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[i]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            Object k;
            <span class="hljs-keyword">if</span> (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.<span class="hljs-keyword">value</span>;
                e.<span class="hljs-keyword">value</span> = <span class="hljs-keyword">value</span>;
                e.recordAccess(<span class="hljs-keyword">this</span>);
                <span class="hljs-keyword">return</span> oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, <span class="hljs-keyword">value</span>, i);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

     <span class="hljs-comment">// putForNullKey()的作用是将“key为null”键值对添加到table[0]位置 </span>
    <span class="hljs-keyword">private</span> V <span class="hljs-title">putForNullKey</span>(V <span class="hljs-keyword">value</span>) {
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[<span class="hljs-number">0</span>]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            <span class="hljs-keyword">if</span> (e.key == <span class="hljs-keyword">null</span>) {
                V oldValue = e.<span class="hljs-keyword">value</span>;
                e.<span class="hljs-keyword">value</span> = <span class="hljs-keyword">value</span>;
                e.recordAccess(<span class="hljs-keyword">this</span>);
                <span class="hljs-keyword">return</span> oldValue;
            }
        }
        modCount++;
        addEntry(<span class="hljs-number">0</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">value</span>, <span class="hljs-number">0</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }

   <span class="hljs-comment">// 创建HashMap对应的“添加方法”,    </span>
    <span class="hljs-comment">// 它和put()不同。putForCreate()是内部方法,它被构造函数等调用,用来创建HashMap    </span>
    <span class="hljs-comment">// 而put()是对外提供的往HashMap中添加元素的方法。</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">putForCreate</span>(K key, V <span class="hljs-keyword">value</span>) {
        <span class="hljs-keyword">int</span> hash = <span class="hljs-keyword">null</span> == key ? <span class="hljs-number">0</span> : hash(key);
        <span class="hljs-keyword">int</span> i = indexFor(hash, table.length);

        <span class="hljs-comment">/**
         * 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.
         */</span>
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[i]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            Object k;
            <span class="hljs-keyword">if</span> (e.hash == hash &&
                ((k = e.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k)))) {
                e.<span class="hljs-keyword">value</span> = <span class="hljs-keyword">value</span>;
                <span class="hljs-keyword">return</span>;
            }
        }

        createEntry(hash, key, <span class="hljs-keyword">value</span>, i);
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">putAllForCreate</span>(Map<? extends K, ? extends V> m) {
        <span class="hljs-keyword">for</span> (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            putForCreate(e.getKey(), e.getValue());
    }

    <span class="hljs-comment">// 重新调整HashMap的大小,newCapacity是调整后的容量</span>
    <span class="hljs-keyword">void</span> resize(<span class="hljs-keyword">int</span> newCapacity) {
        Entry[] oldTable = table;
        <span class="hljs-keyword">int</span> oldCapacity = oldTable.length;
        <span class="hljs-keyword">if</span> (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            <span class="hljs-keyword">return</span>;
        }

        Entry[] newTable = <span class="hljs-keyword">new</span> Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (<span class="hljs-keyword">int</span>)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + <span class="hljs-number">1</span>);
    }

   <span class="hljs-comment">// 将HashMap中的全部元素都添加到newTable中</span>
    <span class="hljs-keyword">void</span> transfer(Entry[] newTable, boolean rehash) {
        <span class="hljs-keyword">int</span> newCapacity = newTable.length;
        <span class="hljs-keyword">for</span> (Entry<K,V> e : table) {
            <span class="hljs-keyword">while</span>(<span class="hljs-keyword">null</span> != e) {
                Entry<K,V> next = e.next;
                <span class="hljs-keyword">if</span> (rehash) {
                    e.hash = <span class="hljs-keyword">null</span> == e.key ? <span class="hljs-number">0</span> : hash(e.key);
                }
                <span class="hljs-keyword">int</span> i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

     <span class="hljs-comment">// 将"m"的全部元素都添加到HashMap中 </span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">putAll</span>(Map<? extends K, ? extends V> m) {
        <span class="hljs-keyword">int</span> numKeysToBeAdded = m.size();
        <span class="hljs-keyword">if</span> (numKeysToBeAdded == <span class="hljs-number">0</span>)
            <span class="hljs-keyword">return</span>;

        <span class="hljs-keyword">if</span> (table == EMPTY_TABLE) {
            inflateTable((<span class="hljs-keyword">int</span>) Math.max(numKeysToBeAdded * loadFactor, threshold));
        }

       <span class="hljs-comment">// 计算容量是否足够,    </span>
        <span class="hljs-comment">// 若“当前阀值容量 < 需要的容量”,则将容量x2。 </span>
        <span class="hljs-keyword">if</span> (numKeysToBeAdded > threshold) {
            <span class="hljs-keyword">int</span> targetCapacity = (<span class="hljs-keyword">int</span>)(numKeysToBeAdded / loadFactor + <span class="hljs-number">1</span>);
            <span class="hljs-keyword">if</span> (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            <span class="hljs-keyword">int</span> newCapacity = table.length;
            <span class="hljs-keyword">while</span> (newCapacity < targetCapacity)
                newCapacity <<= <span class="hljs-number">1</span>;
            <span class="hljs-keyword">if</span> (newCapacity > table.length)
                resize(newCapacity);
        }

        <span class="hljs-keyword">for</span> (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    <span class="hljs-comment">// 删除“键为key”元素  </span>
    <span class="hljs-keyword">public</span> V <span class="hljs-title">remove</span>(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        <span class="hljs-keyword">return</span> (e == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">null</span> : e.<span class="hljs-keyword">value</span>);
    }

    <span class="hljs-comment">// 删除“键为key”的元素 </span>
    final Entry<K,V> removeEntryForKey(Object key) {
        <span class="hljs-keyword">if</span> (size == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
        <span class="hljs-keyword">int</span> hash = (key == <span class="hljs-keyword">null</span>) ? <span class="hljs-number">0</span> : hash(key);
        <span class="hljs-keyword">int</span> i = indexFor(hash, table.length);
        Entry<K,V> prev = table[i];
        Entry<K,V> e = prev;

        <span class="hljs-keyword">while</span> (e != <span class="hljs-keyword">null</span>) {
            Entry<K,V> next = e.next;
            Object k;
            <span class="hljs-keyword">if</span> (e.hash == hash &&
                ((k = e.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k)))) {
                modCount++;
                size--;
                <span class="hljs-keyword">if</span> (prev == e)
                    table[i] = next;
                <span class="hljs-keyword">else</span>
                    prev.next = next;
                e.recordRemoval(<span class="hljs-keyword">this</span>);
                <span class="hljs-keyword">return</span> e;
            }
            prev = e;
            e = next;
        }

        <span class="hljs-keyword">return</span> e;
    }

    <span class="hljs-comment">// 删除“键值对” </span>
    final Entry<K,V> removeMapping(Object o) {
        <span class="hljs-keyword">if</span> (size == <span class="hljs-number">0</span> || !(o instanceof Map.Entry))
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;

        Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
        Object key = entry.getKey();
        <span class="hljs-keyword">int</span> hash = (key == <span class="hljs-keyword">null</span>) ? <span class="hljs-number">0</span> : hash(key);
        <span class="hljs-keyword">int</span> i = indexFor(hash, table.length);
        Entry<K,V> prev = table[i];
        Entry<K,V> e = prev;

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

        <span class="hljs-keyword">return</span> e;
    }

     <span class="hljs-comment">// 清空HashMap,将所有的元素设为null</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clear</span>() {
        modCount++;
        Arrays.fill(table, <span class="hljs-keyword">null</span>);
        size = <span class="hljs-number">0</span>;
    }

   <span class="hljs-comment">// 是否包含“值为value”的元素 </span>
    <span class="hljs-keyword">public</span> boolean <span class="hljs-title">containsValue</span>(Object <span class="hljs-keyword">value</span>) {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">value</span> == <span class="hljs-keyword">null</span>)
            <span class="hljs-keyword">return</span> containsNullValue();

        Entry[] tab = table;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < tab.length ; i++)
            <span class="hljs-keyword">for</span> (Entry e = tab[i] ; e != <span class="hljs-keyword">null</span> ; e = e.next)
                <span class="hljs-keyword">if</span> (<span class="hljs-keyword">value</span>.equals(e.<span class="hljs-keyword">value</span>))
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }

    <span class="hljs-comment">// 是否包含null值 </span>
    <span class="hljs-keyword">private</span> boolean <span class="hljs-title">containsNullValue</span>() {
        Entry[] tab = table;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < tab.length ; i++)
            <span class="hljs-keyword">for</span> (Entry e = tab[i] ; e != <span class="hljs-keyword">null</span> ; e = e.next)
                <span class="hljs-keyword">if</span> (e.<span class="hljs-keyword">value</span> == <span class="hljs-keyword">null</span>)
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }

    <span class="hljs-comment">// 克隆一个HashMap,并返回Object对象  </span>
    <span class="hljs-keyword">public</span> Object <span class="hljs-title">clone</span>() {
        HashMap<K,V> result = <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">try</span> {
            result = (HashMap<K,V>)super.clone();
        } <span class="hljs-keyword">catch</span> (CloneNotSupportedException e) {
            <span class="hljs-comment">// assert false;</span>
        }
        <span class="hljs-keyword">if</span> (result.table != EMPTY_TABLE) {
            result.inflateTable(Math.min(
                (<span class="hljs-keyword">int</span>) Math.min(
                    size * Math.min(<span class="hljs-number">1</span> / loadFactor, <span class="hljs-number">4.0</span>f),
                    <span class="hljs-comment">// we have limits...</span>
                    HashMap.MAXIMUM_CAPACITY),
               table.length));
        }
        result.entrySet = <span class="hljs-keyword">null</span>;
        result.modCount = <span class="hljs-number">0</span>;
        result.size = <span class="hljs-number">0</span>;
        result.init();
        result.putAllForCreate(<span class="hljs-keyword">this</span>);

        <span class="hljs-keyword">return</span> result;
    }
 <span class="hljs-comment">// Entry是单向链表。    </span>
    <span class="hljs-comment">// 它是 “HashMap链式存储法”对应的链表。    </span>
    <span class="hljs-comment">// 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数</span>
    <span class="hljs-keyword">static</span> class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V <span class="hljs-keyword">value</span>;
        Entry<K,V> next;
        <span class="hljs-keyword">int</span> hash;

        <span class="hljs-comment">/**
         * Creates new entry.
         */</span>
        Entry(<span class="hljs-keyword">int</span> h, K k, V v, Entry<K,V> n) {
            <span class="hljs-keyword">value</span> = v;
            next = n;
            key = k;
            hash = h;
        }

        <span class="hljs-keyword">public</span> final K <span class="hljs-title">getKey</span>() {
            <span class="hljs-keyword">return</span> key;
        }

        <span class="hljs-keyword">public</span> final V <span class="hljs-title">getValue</span>() {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">value</span>;
        }

        <span class="hljs-keyword">public</span> final V <span class="hljs-title">setValue</span>(V newValue) {
            V oldValue = <span class="hljs-keyword">value</span>;
            <span class="hljs-keyword">value</span> = newValue;
            <span class="hljs-keyword">return</span> oldValue;
        }

        <span class="hljs-keyword">public</span> final boolean <span class="hljs-title">equals</span>(Object o) {
            <span class="hljs-keyword">if</span> (!(o instanceof Map.Entry))
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            <span class="hljs-keyword">if</span> (k1 == k2 || (k1 != <span class="hljs-keyword">null</span> && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                <span class="hljs-keyword">if</span> (v1 == v2 || (v1 != <span class="hljs-keyword">null</span> && v1.equals(v2)))
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            }
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }

        <span class="hljs-keyword">public</span> final <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span>() {
            <span class="hljs-keyword">return</span> Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        <span class="hljs-keyword">public</span> final String <span class="hljs-title">toString</span>() {
            <span class="hljs-keyword">return</span> getKey() + <span class="hljs-string">"="</span> + getValue();
        }

        <span class="hljs-comment">// 当向HashMap中添加元素时,会调用recordAccess()。    </span>
    <span class="hljs-comment">// 这里不做任何处理 </span>
        <span class="hljs-keyword">void</span> recordAccess(HashMap<K,V> m) {
        }

        <span class="hljs-comment">// 当从HashMap中删除元素时,绘调用recordRemoval()。    </span>
    <span class="hljs-comment">// 这里不做任何处理</span>
        <span class="hljs-keyword">void</span> recordRemoval(HashMap<K,V> m) {
        }
    }

    <span class="hljs-comment">// 新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。</span>
    <span class="hljs-keyword">void</span> addEntry(<span class="hljs-keyword">int</span> hash, K key, V <span class="hljs-keyword">value</span>, <span class="hljs-keyword">int</span> bucketIndex) {
        <span class="hljs-keyword">if</span> ((size >= threshold) && (<span class="hljs-keyword">null</span> != table[bucketIndex])) {
            resize(<span class="hljs-number">2</span> * table.length);
            hash = (<span class="hljs-keyword">null</span> != key) ? hash(key) : <span class="hljs-number">0</span>;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, <span class="hljs-keyword">value</span>, bucketIndex);
    }

      <span class="hljs-comment">// 创建Entry。将“key-value”插入指定位置。</span>
    <span class="hljs-keyword">void</span> createEntry(<span class="hljs-keyword">int</span> hash, K key, V <span class="hljs-keyword">value</span>, <span class="hljs-keyword">int</span> bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = <span class="hljs-keyword">new</span> Entry<>(hash, key, <span class="hljs-keyword">value</span>, e);
        size++;
    }

<span class="hljs-comment">// HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。    </span>
    <span class="hljs-comment">// 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> HashIterator<E> implements Iterator<E> {
        Entry<K,V> next;        <span class="hljs-comment">//  下一个元素    </span>
        <span class="hljs-keyword">int</span> expectedModCount;   <span class="hljs-comment">//expectedModCount用于实现fast-fail机制。 </span>
        <span class="hljs-keyword">int</span> index;              <span class="hljs-comment">// 当前索引    </span>
        Entry<K,V> current;     <span class="hljs-comment">// 当前元素 </span>

        HashIterator() {
            expectedModCount = modCount;
            <span class="hljs-keyword">if</span> (size > <span class="hljs-number">0</span>) { <span class="hljs-comment">// advance to first entry</span>
                Entry[] t = table;
                <span class="hljs-comment">// 将next指向table中第一个不为null的元素。    </span>
                <span class="hljs-comment">// 这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环</span>
                <span class="hljs-keyword">while</span> (index < t.length && (next = t[index++]) == <span class="hljs-keyword">null</span>)
                    ;
            }
        }

        <span class="hljs-keyword">public</span> final boolean <span class="hljs-title">hasNext</span>() {
            <span class="hljs-keyword">return</span> next != <span class="hljs-keyword">null</span>;
        }
        <span class="hljs-comment">// 获取下一个元素  </span>
        final Entry<K,V> nextEntry() {
            <span class="hljs-keyword">if</span> (modCount != expectedModCount)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ConcurrentModificationException();
            Entry<K,V> e = next;
            <span class="hljs-keyword">if</span> (e == <span class="hljs-keyword">null</span>)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NoSuchElementException();
    <span class="hljs-comment">// 注意!!!    </span>
            <span class="hljs-comment">// 一个Entry就是一个单向链表    </span>
            <span class="hljs-comment">// 若该Entry的下一个节点不为空,就将next指向下一个节点;    </span>
            <span class="hljs-comment">// 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。</span>
            <span class="hljs-keyword">if</span> ((next = e.next) == <span class="hljs-keyword">null</span>) {
                Entry[] t = table;
                <span class="hljs-keyword">while</span> (index < t.length && (next = t[index++]) == <span class="hljs-keyword">null</span>)
                    ;
            }
            current = e;
            <span class="hljs-keyword">return</span> e;
        }
        <span class="hljs-comment">//删除当前元素  </span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">remove</span>() {
            <span class="hljs-keyword">if</span> (current == <span class="hljs-keyword">null</span>)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalStateException();
            <span class="hljs-keyword">if</span> (modCount != expectedModCount)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ConcurrentModificationException();
            Object k = current.key;
            current = <span class="hljs-keyword">null</span>;
            HashMap.<span class="hljs-keyword">this</span>.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }
    <span class="hljs-comment">// value的迭代器  </span>
    <span class="hljs-keyword">private</span> final <span class="hljs-keyword">class</span> ValueIterator extends HashIterator<V> {
        <span class="hljs-keyword">public</span> V <span class="hljs-title">next</span>() {
            <span class="hljs-keyword">return</span> nextEntry().<span class="hljs-keyword">value</span>;
        }
    }
     <span class="hljs-comment">// key的迭代器  </span>
    <span class="hljs-keyword">private</span> final <span class="hljs-keyword">class</span> KeyIterator extends HashIterator<K> {
        <span class="hljs-keyword">public</span> K <span class="hljs-title">next</span>() {
            <span class="hljs-keyword">return</span> nextEntry().getKey();
        }
    }
    <span class="hljs-comment">// Entry的迭代器</span>
    <span class="hljs-keyword">private</span> final <span class="hljs-keyword">class</span> EntryIterator extends HashIterator<Map.Entry<K,V>> {
        <span class="hljs-keyword">public</span> Map.Entry<K,V> <span class="hljs-title">next</span>() {
            <span class="hljs-keyword">return</span> nextEntry();
        }
    }

    <span class="hljs-comment">// 返回一个“key迭代器” </span>
    Iterator<K> newKeyIterator()   {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> KeyIterator();
    }
    <span class="hljs-comment">//返回一个“value迭代器” </span>
    Iterator<V> newValueIterator()   {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ValueIterator();
    }
     <span class="hljs-comment">// 返回一个“entry迭代器” </span>
    Iterator<Map.Entry<K,V>> newEntryIterator()   {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> EntryIterator();
    }


    <span class="hljs-comment">// Views</span>

    <span class="hljs-keyword">private</span> transient Set<Map.Entry<K,V>> entrySet = <span class="hljs-keyword">null</span>;

    <span class="hljs-comment">// 返回“key的集合”,实际上返回一个“KeySet对象”  </span>
    <span class="hljs-keyword">public</span> Set<K> <span class="hljs-title">keySet</span>() {
        Set<K> ks = keySet;
        <span class="hljs-keyword">return</span> (ks != <span class="hljs-keyword">null</span> ? ks : (keySet = <span class="hljs-keyword">new</span> KeySet()));
    }
    <span class="hljs-comment">// Key对应的集合    </span>
    <span class="hljs-comment">// KeySet继承于AbstractSet,说明该集合中没有重复的Key。</span>
    <span class="hljs-keyword">private</span> final <span class="hljs-keyword">class</span> KeySet extends AbstractSet<K> {
        <span class="hljs-keyword">public</span> Iterator<K> <span class="hljs-title">iterator</span>() {
            <span class="hljs-keyword">return</span> newKeyIterator();
        }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">size</span>() {
            <span class="hljs-keyword">return</span> size;
        }
        <span class="hljs-keyword">public</span> boolean <span class="hljs-title">contains</span>(Object o) {
            <span class="hljs-keyword">return</span> containsKey(o);
        }
        <span class="hljs-keyword">public</span> boolean <span class="hljs-title">remove</span>(Object o) {
            <span class="hljs-keyword">return</span> HashMap.<span class="hljs-keyword">this</span>.removeEntryForKey(o) != <span class="hljs-keyword">null</span>;
        }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clear</span>() {
            HashMap.<span class="hljs-keyword">this</span>.clear();
        }
    }

     <span class="hljs-comment">// 返回“value集合”,实际上返回的是一个Values对象   </span>
    <span class="hljs-keyword">public</span> Collection<V> <span class="hljs-title">values</span>() {
        Collection<V> vs = values;
        <span class="hljs-keyword">return</span> (vs != <span class="hljs-keyword">null</span> ? vs : (values = <span class="hljs-keyword">new</span> Values()));
    }

    <span class="hljs-keyword">private</span> final <span class="hljs-keyword">class</span> Values extends AbstractCollection<V> {
        <span class="hljs-keyword">public</span> Iterator<V> <span class="hljs-title">iterator</span>() {
            <span class="hljs-keyword">return</span> newValueIterator();
        }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">size</span>() {
            <span class="hljs-keyword">return</span> size;
        }
        <span class="hljs-keyword">public</span> boolean <span class="hljs-title">contains</span>(Object o) {
            <span class="hljs-keyword">return</span> containsValue(o);
        }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clear</span>() {
            HashMap.<span class="hljs-keyword">this</span>.clear();
        }
    }

    <span class="hljs-comment">// 返回“HashMap的Entry集合”</span>
    <span class="hljs-keyword">public</span> Set<Map.Entry<K,V>> <span class="hljs-title">entrySet</span>() {
        <span class="hljs-keyword">return</span> entrySet0();
    }
    <span class="hljs-comment">// 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象</span>
    <span class="hljs-keyword">private</span> Set<Map.Entry<K,V>> <span class="hljs-title">entrySet0</span>() {
        Set<Map.Entry<K,V>> es = entrySet;
        <span class="hljs-keyword">return</span> es != <span class="hljs-keyword">null</span> ? es : (entrySet = <span class="hljs-keyword">new</span> EntrySet());
    }

    <span class="hljs-keyword">private</span> final <span class="hljs-keyword">class</span> EntrySet extends AbstractSet<Map.Entry<K,V>> {
        <span class="hljs-keyword">public</span> Iterator<Map.Entry<K,V>> <span class="hljs-title">iterator</span>() {
            <span class="hljs-keyword">return</span> newEntryIterator();
        }
        <span class="hljs-keyword">public</span> boolean <span class="hljs-title">contains</span>(Object o) {
            <span class="hljs-keyword">if</span> (!(o instanceof Map.Entry))
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
            Entry<K,V> candidate = getEntry(e.getKey());
            <span class="hljs-keyword">return</span> candidate != <span class="hljs-keyword">null</span> && candidate.equals(e);
        }
        <span class="hljs-keyword">public</span> boolean <span class="hljs-title">remove</span>(Object o) {
            <span class="hljs-keyword">return</span> removeMapping(o) != <span class="hljs-keyword">null</span>;
        }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">size</span>() {
            <span class="hljs-keyword">return</span> size;
        }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clear</span>() {
            HashMap.<span class="hljs-keyword">this</span>.clear();
        }
    }

    <span class="hljs-comment">// java.io.Serializable的写入函数    </span>
    <span class="hljs-comment">// 将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">writeObject</span>(java.io.ObjectOutputStream s)
        throws IOException
    {
        <span class="hljs-comment">// Write out the threshold, loadfactor, and any hidden stuff</span>
        s.defaultWriteObject();

        <span class="hljs-comment">// Write out number of buckets</span>
        <span class="hljs-keyword">if</span> (table==EMPTY_TABLE) {
            s.writeInt(roundUpToPowerOf2(threshold));
        } <span class="hljs-keyword">else</span> {
           s.writeInt(table.length);
        }

        <span class="hljs-comment">// Write out size (number of Mappings)</span>
        s.writeInt(size);

        <span class="hljs-comment">// Write out keys and values (alternating)</span>
        <span class="hljs-keyword">if</span> (size > <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">for</span>(Map.Entry<K,V> e : entrySet0()) {
                s.writeObject(e.getKey());
                s.writeObject(e.getValue());
            }
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> final <span class="hljs-keyword">long</span> serialVersionUID = <span class="hljs-number">362498820763181265</span>L;

     <span class="hljs-comment">// java.io.Serializable的读取函数:根据写入方式读出    </span>
    <span class="hljs-comment">// 将HashMap的“总的容量,实际容量,所有的Entry”依次读出</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">readObject</span>(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
        <span class="hljs-comment">// Read in the threshold (ignored), loadfactor, and any hidden stuff</span>
        s.defaultReadObject();
        <span class="hljs-keyword">if</span> (loadFactor <= <span class="hljs-number">0</span> || Float.isNaN(loadFactor)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidObjectException(<span class="hljs-string">"Illegal load factor: "</span> +
                                               loadFactor);
        }

        <span class="hljs-comment">// set other fields that need values</span>
        table = (Entry<K,V>[]) EMPTY_TABLE;

        <span class="hljs-comment">// Read in number of buckets</span>
        s.readInt(); <span class="hljs-comment">// ignored.</span>

        <span class="hljs-comment">// Read number of mappings</span>
        <span class="hljs-keyword">int</span> mappings = s.readInt();
        <span class="hljs-keyword">if</span> (mappings < <span class="hljs-number">0</span>)
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidObjectException(<span class="hljs-string">"Illegal mappings count: "</span> +
                                               mappings);

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

        <span class="hljs-comment">// allocate the bucket array;</span>
        <span class="hljs-keyword">if</span> (mappings > <span class="hljs-number">0</span>) {
            inflateTable(capacity);
        } <span class="hljs-keyword">else</span> {
            threshold = capacity;
        }

        init();  <span class="hljs-comment">// Give subclass a chance to do its thing.</span>

        <span class="hljs-comment">// Read the keys and values, and put the mappings in the HashMap</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < mappings; i++) {
            K key = (K) s.readObject();
            V <span class="hljs-keyword">value</span> = (V) s.readObject();
            putForCreate(key, <span class="hljs-keyword">value</span>);
        }
    }

   <span class="hljs-comment">// 返回“HashMap总的容量” </span>
    <span class="hljs-keyword">int</span>   capacity()     { <span class="hljs-keyword">return</span> table.length; }
     <span class="hljs-comment">// 返回“HashMap的加载因子”   </span>
    <span class="hljs-keyword">float</span> loadFactor()   { <span class="hljs-keyword">return</span> loadFactor;   }
}

</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li><li>132</li><li>133</li><li>134</li><li>135</li><li>136</li><li>137</li><li>138</li><li>139</li><li>140</li><li>141</li><li>142</li><li>143</li><li>144</li><li>145</li><li>146</li><li>147</li><li>148</li><li>149</li><li>150</li><li>151</li><li>152</li><li>153</li><li>154</li><li>155</li><li>156</li><li>157</li><li>158</li><li>159</li><li>160</li><li>161</li><li>162</li><li>163</li><li>164</li><li>165</li><li>166</li><li>167</li><li>168</li><li>169</li><li>170</li><li>171</li><li>172</li><li>173</li><li>174</li><li>175</li><li>176</li><li>177</li><li>178</li><li>179</li><li>180</li><li>181</li><li>182</li><li>183</li><li>184</li><li>185</li><li>186</li><li>187</li><li>188</li><li>189</li><li>190</li><li>191</li><li>192</li><li>193</li><li>194</li><li>195</li><li>196</li><li>197</li><li>198</li><li>199</li><li>200</li><li>201</li><li>202</li><li>203</li><li>204</li><li>205</li><li>206</li><li>207</li><li>208</li><li>209</li><li>210</li><li>211</li><li>212</li><li>213</li><li>214</li><li>215</li><li>216</li><li>217</li><li>218</li><li>219</li><li>220</li><li>221</li><li>222</li><li>223</li><li>224</li><li>225</li><li>226</li><li>227</li><li>228</li><li>229</li><li>230</li><li>231</li><li>232</li><li>233</li><li>234</li><li>235</li><li>236</li><li>237</li><li>238</li><li>239</li><li>240</li><li>241</li><li>242</li><li>243</li><li>244</li><li>245</li><li>246</li><li>247</li><li>248</li><li>249</li><li>250</li><li>251</li><li>252</li><li>253</li><li>254</li><li>255</li><li>256</li><li>257</li><li>258</li><li>259</li><li>260</li><li>261</li><li>262</li><li>263</li><li>264</li><li>265</li><li>266</li><li>267</li><li>268</li><li>269</li><li>270</li><li>271</li><li>272</li><li>273</li><li>274</li><li>275</li><li>276</li><li>277</li><li>278</li><li>279</li><li>280</li><li>281</li><li>282</li><li>283</li><li>284</li><li>285</li><li>286</li><li>287</li><li>288</li><li>289</li><li>290</li><li>291</li><li>292</li><li>293</li><li>294</li><li>295</li><li>296</li><li>297</li><li>298</li><li>299</li><li>300</li><li>301</li><li>302</li><li>303</li><li>304</li><li>305</li><li>306</li><li>307</li><li>308</li><li>309</li><li>310</li><li>311</li><li>312</li><li>313</li><li>314</li><li>315</li><li>316</li><li>317</li><li>318</li><li>319</li><li>320</li><li>321</li><li>322</li><li>323</li><li>324</li><li>325</li><li>326</li><li>327</li><li>328</li><li>329</li><li>330</li><li>331</li><li>332</li><li>333</li><li>334</li><li>335</li><li>336</li><li>337</li><li>338</li><li>339</li><li>340</li><li>341</li><li>342</li><li>343</li><li>344</li><li>345</li><li>346</li><li>347</li><li>348</li><li>349</li><li>350</li><li>351</li><li>352</li><li>353</li><li>354</li><li>355</li><li>356</li><li>357</li><li>358</li><li>359</li><li>360</li><li>361</li><li>362</li><li>363</li><li>364</li><li>365</li><li>366</li><li>367</li><li>368</li><li>369</li><li>370</li><li>371</li><li>372</li><li>373</li><li>374</li><li>375</li><li>376</li><li>377</li><li>378</li><li>379</li><li>380</li><li>381</li><li>382</li><li>383</li><li>384</li><li>385</li><li>386</li><li>387</li><li>388</li><li>389</li><li>390</li><li>391</li><li>392</li><li>393</li><li>394</li><li>395</li><li>396</li><li>397</li><li>398</li><li>399</li><li>400</li><li>401</li><li>402</li><li>403</li><li>404</li><li>405</li><li>406</li><li>407</li><li>408</li><li>409</li><li>410</li><li>411</li><li>412</li><li>413</li><li>414</li><li>415</li><li>416</li><li>417</li><li>418</li><li>419</li><li>420</li><li>421</li><li>422</li><li>423</li><li>424</li><li>425</li><li>426</li><li>427</li><li>428</li><li>429</li><li>430</li><li>431</li><li>432</li><li>433</li><li>434</li><li>435</li><li>436</li><li>437</li><li>438</li><li>439</li><li>440</li><li>441</li><li>442</li><li>443</li><li>444</li><li>445</li><li>446</li><li>447</li><li>448</li><li>449</li><li>450</li><li>451</li><li>452</li><li>453</li><li>454</li><li>455</li><li>456</li><li>457</li><li>458</li><li>459</li><li>460</li><li>461</li><li>462</li><li>463</li><li>464</li><li>465</li><li>466</li><li>467</li><li>468</li><li>469</li><li>470</li><li>471</li><li>472</li><li>473</li><li>474</li><li>475</li><li>476</li><li>477</li><li>478</li><li>479</li><li>480</li><li>481</li><li>482</li><li>483</li><li>484</li><li>485</li><li>486</li><li>487</li><li>488</li><li>489</li><li>490</li><li>491</li><li>492</li><li>493</li><li>494</li><li>495</li><li>496</li><li>497</li><li>498</li><li>499</li><li>500</li><li>501</li><li>502</li><li>503</li><li>504</li><li>505</li><li>506</li><li>507</li><li>508</li><li>509</li><li>510</li><li>511</li><li>512</li><li>513</li><li>514</li><li>515</li><li>516</li><li>517</li><li>518</li><li>519</li><li>520</li><li>521</li><li>522</li><li>523</li><li>524</li><li>525</li><li>526</li><li>527</li><li>528</li><li>529</li><li>530</li><li>531</li><li>532</li><li>533</li><li>534</li><li>535</li><li>536</li><li>537</li><li>538</li><li>539</li><li>540</li><li>541</li><li>542</li><li>543</li><li>544</li><li>545</li><li>546</li><li>547</li><li>548</li><li>549</li><li>550</li><li>551</li><li>552</li><li>553</li><li>554</li><li>555</li><li>556</li><li>557</li><li>558</li><li>559</li><li>560</li><li>561</li><li>562</li><li>563</li><li>564</li><li>565</li><li>566</li><li>567</li><li>568</li><li>569</li><li>570</li><li>571</li><li>572</li><li>573</li><li>574</li><li>575</li><li>576</li><li>577</li><li>578</li><li>579</li><li>580</li><li>581</li><li>582</li><li>583</li><li>584</li><li>585</li><li>586</li><li>587</li><li>588</li><li>589</li><li>590</li><li>591</li><li>592</li><li>593</li><li>594</li><li>595</li><li>596</li><li>597</li><li>598</li><li>599</li><li>600</li><li>601</li><li>602</li><li>603</li><li>604</li><li>605</li><li>606</li><li>607</li><li>608</li><li>609</li><li>610</li><li>611</li><li>612</li><li>613</li><li>614</li><li>615</li><li>616</li><li>617</li><li>618</li><li>619</li><li>620</li><li>621</li><li>622</li><li>623</li><li>624</li><li>625</li><li>626</li><li>627</li><li>628</li><li>629</li><li>630</li><li>631</li><li>632</li><li>633</li><li>634</li><li>635</li><li>636</li><li>637</li><li>638</li><li>639</li><li>640</li><li>641</li><li>642</li><li>643</li><li>644</li><li>645</li><li>646</li><li>647</li><li>648</li><li>649</li><li>650</li><li>651</li><li>652</li><li>653</li><li>654</li><li>655</li><li>656</li><li>657</li><li>658</li><li>659</li><li>660</li><li>661</li><li>662</li><li>663</li><li>664</li><li>665</li><li>666</li><li>667</li><li>668</li><li>669</li><li>670</li><li>671</li><li>672</li><li>673</li><li>674</li><li>675</li><li>676</li><li>677</li><li>678</li><li>679</li><li>680</li><li>681</li><li>682</li><li>683</li><li>684</li><li>685</li><li>686</li><li>687</li><li>688</li><li>689</li><li>690</li><li>691</li><li>692</li><li>693</li><li>694</li><li>695</li><li>696</li><li>697</li><li>698</li><li>699</li><li>700</li><li>701</li><li>702</li><li>703</li><li>704</li><li>705</li><li>706</li><li>707</li><li>708</li><li>709</li><li>710</li><li>711</li><li>712</li><li>713</li><li>714</li><li>715</li><li>716</li><li>717</li><li>718</li><li>719</li><li>720</li><li>721</li><li>722</li><li>723</li><li>724</li><li>725</li><li>726</li><li>727</li><li>728</li><li>729</li><li>730</li><li>731</li><li>732</li><li>733</li><li>734</li><li>735</li><li>736</li><li>737</li><li>738</li><li>739</li><li>740</li><li>741</li><li>742</li><li>743</li><li>744</li><li>745</li><li>746</li><li>747</li><li>748</li><li>749</li><li>750</li><li>751</li><li>752</li><li>753</li><li>754</li><li>755</li><li>756</li><li>757</li><li>758</li><li>759</li><li>760</li><li>761</li><li>762</li><li>763</li><li>764</li><li>765</li><li>766</li><li>767</li><li>768</li><li>769</li><li>770</li><li>771</li><li>772</li><li>773</li><li>774</li><li>775</li><li>776</li><li>777</li><li>778</li><li>779</li><li>780</li><li>781</li><li>782</li><li>783</li><li>784</li><li>785</li><li>786</li><li>787</li><li>788</li><li>789</li><li>790</li><li>791</li><li>792</li><li>793</li><li>794</li><li>795</li><li>796</li><li>797</li><li>798</li><li>799</li><li>800</li><li>801</li><li>802</li><li>803</li><li>804</li><li>805</li><li>806</li><li>807</li><li>808</li><li>809</li><li>810</li><li>811</li><li>812</li><li>813</li><li>814</li><li>815</li><li>816</li><li>817</li><li>818</li><li>819</li><li>820</li><li>821</li><li>822</li><li>823</li><li>824</li><li>825</li><li>826</li><li>827</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

HashMap重点解析

1. HashMap存储结构

HashMap存储结构如下图所示
HashMap存储结构

2. 链表中节点的数据结构

链表中节点的数据结构定义如下:

<code class="hljs cs has-numbering"><span class="hljs-comment">// Entry是单向链表。    </span>
    <span class="hljs-comment">// 它是 “HashMap链式存储法”对应的链表。    </span>
    <span class="hljs-comment">// 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数</span>
    <span class="hljs-keyword">static</span> class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V <span class="hljs-keyword">value</span>;
        Entry<K,V> next;
        <span class="hljs-keyword">int</span> hash;

        <span class="hljs-comment">// 构造函数。    </span>
    <span class="hljs-comment">// 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"    </span>
        Entry(<span class="hljs-keyword">int</span> h, K k, V v, Entry<K,V> n) {
            <span class="hljs-keyword">value</span> = v;
            next = n;
            key = k;
            hash = h;
        }

        <span class="hljs-keyword">public</span> final K <span class="hljs-title">getKey</span>() {
            <span class="hljs-keyword">return</span> key;
        }

        <span class="hljs-keyword">public</span> final V <span class="hljs-title">getValue</span>() {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">value</span>;
        }

        <span class="hljs-keyword">public</span> final V <span class="hljs-title">setValue</span>(V newValue) {
            V oldValue = <span class="hljs-keyword">value</span>;
            <span class="hljs-keyword">value</span> = newValue;
            <span class="hljs-keyword">return</span> oldValue;
        }
 <span class="hljs-comment">// 判断两个Entry是否相等    </span>
    <span class="hljs-comment">// 若两个Entry的“key”和“value”都相等,则返回true。    </span>
    <span class="hljs-comment">// 否则,返回false  </span>
        <span class="hljs-keyword">public</span> final boolean <span class="hljs-title">equals</span>(Object o) {
            <span class="hljs-keyword">if</span> (!(o instanceof Map.Entry))
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            <span class="hljs-keyword">if</span> (k1 == k2 || (k1 != <span class="hljs-keyword">null</span> && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                <span class="hljs-keyword">if</span> (v1 == v2 || (v1 != <span class="hljs-keyword">null</span> && v1.equals(v2)))
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            }
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }
  <span class="hljs-comment">// 实现hashCode()  </span>
        <span class="hljs-keyword">public</span> final <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span>() {
            <span class="hljs-keyword">return</span> Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        <span class="hljs-keyword">public</span> final String <span class="hljs-title">toString</span>() {
            <span class="hljs-keyword">return</span> getKey() + <span class="hljs-string">"="</span> + getValue();
        }

        <span class="hljs-comment">// 当向HashMap中添加元素时,会调用recordAccess()。    </span>
    <span class="hljs-comment">// 这里不做任何处理 </span>
        <span class="hljs-keyword">void</span> recordAccess(HashMap<K,V> m) {
        }

        <span class="hljs-comment">// 当从HashMap中删除元素时,绘调用recordRemoval()。    </span>
    <span class="hljs-comment">// 这里不做任何处理</span>
        <span class="hljs-keyword">void</span> recordRemoval(HashMap<K,V> m) {
        }
    }

    <span class="hljs-comment">// 新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。</span>
    <span class="hljs-keyword">void</span> addEntry(<span class="hljs-keyword">int</span> hash, K key, V <span class="hljs-keyword">value</span>, <span class="hljs-keyword">int</span> bucketIndex) {
        <span class="hljs-keyword">if</span> ((size >= threshold) && (<span class="hljs-keyword">null</span> != table[bucketIndex])) {
            resize(<span class="hljs-number">2</span> * table.length);
            hash = (<span class="hljs-keyword">null</span> != key) ? hash(key) : <span class="hljs-number">0</span>;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, <span class="hljs-keyword">value</span>, bucketIndex);
    }

      <span class="hljs-comment">// 创建Entry。将“key-value”插入指定位置。</span>
    <span class="hljs-keyword">void</span> createEntry(<span class="hljs-keyword">int</span> hash, K key, V <span class="hljs-keyword">value</span>, <span class="hljs-keyword">int</span> bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = <span class="hljs-keyword">new</span> Entry<>(hash, key, <span class="hljs-keyword">value</span>, e);
        size++;
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

它的结构元素除了key、value、hash外,还有next,next指向下一个节点。另外,这里覆写了equals和hashCode方法来保证键值对的独一无二。

3. 初始容量和加载因子

HashMap共有四个构造方法。构造方法中提到了两个很重要的参数:初始容量加载因子。这两个参数是影响HashMap性能的重要参数,其中容量表示哈希表中槽的数量(即哈希数组的长度),初始容量是创建哈希表时的容量(从构造函数中可以看出,如果不指明,则默认为16),加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 resize 操作(即扩容)。

下面说下加载因子,如果加载因子越大,对空间的利用更充分,但是查找效率会降低(链表长度会越来越长);如果加载因子太小,那么表中的数据将过于稀疏(很多空间还没用,就开始扩容了),对空间造成严重浪费。如果我们在构造方法中不指定,则系统默认加载因子为0.75,这是一个比较理想的值,一般情况下我们是无需修改的。

另外,无论我们指定的容量为多少,构造方法都会将实际容量设为不小于指定容量的2的次方的一个数,且最大值不能超过2的30次方。

4. HashMap中key和value都允许为null

5. put/get方法实现方式

get操作
<code class="hljs cs has-numbering"> <span class="hljs-comment">// 获取key对应的value </span>
    <span class="hljs-keyword">public</span> V <span class="hljs-title">get</span>(Object key) {
        <span class="hljs-keyword">if</span> (key == <span class="hljs-keyword">null</span>)
            <span class="hljs-keyword">return</span> getForNullKey();
        Entry<K,V> entry = getEntry(key);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span> == entry ? <span class="hljs-keyword">null</span> : entry.getValue();
    }
     <span class="hljs-comment"><span class="hljs-xmlDocTag">///</span>/ 获取“key为null”的元素的值    </span>
    <span class="hljs-comment">// HashMap将“key为null”的元素存储在table[0]位置,但不一定是该链表的第一个位置!</span>
    <span class="hljs-keyword">private</span> V <span class="hljs-title">getForNullKey</span>() {
        <span class="hljs-keyword">if</span> (size == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[<span class="hljs-number">0</span>]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            <span class="hljs-keyword">if</span> (e.key == <span class="hljs-keyword">null</span>)
                <span class="hljs-keyword">return</span> e.<span class="hljs-keyword">value</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

首先,如果key为null,则直接从哈希表的第一个位置table[0]对应的链表上查找。记住,key为null的键值对永远都放在以table[0]为头结点的链表中,当然不一定是存放在头结点table[0]中。

如果key不为null,则先求的key的hash值,根据hash值找到在table中的索引,在该索引对应的单链表中查找是否有键值对的key与目标key相等,有就返回对应的value,没有则返回null。

put操作
<code class="hljs cs has-numbering">  <span class="hljs-comment">// 将“key-value”添加到HashMap中 </span>
    <span class="hljs-keyword">public</span> V <span class="hljs-title">put</span>(K key, V <span class="hljs-keyword">value</span>) {
        <span class="hljs-keyword">if</span> (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        <span class="hljs-comment">// 若“key为null”,则将该键值对添加到table[0]中。</span>
        <span class="hljs-keyword">if</span> (key == <span class="hljs-keyword">null</span>)
            <span class="hljs-keyword">return</span> putForNullKey(<span class="hljs-keyword">value</span>);
             <span class="hljs-comment">// 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。 </span>
        <span class="hljs-keyword">int</span> hash = hash(key);
        <span class="hljs-keyword">int</span> i = indexFor(hash, table.length);
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[i]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            Object k;
            <span class="hljs-comment">// 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!</span>
            <span class="hljs-keyword">if</span> (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.<span class="hljs-keyword">value</span>;
                e.<span class="hljs-keyword">value</span> = <span class="hljs-keyword">value</span>;
                e.recordAccess(<span class="hljs-keyword">this</span>);
                <span class="hljs-keyword">return</span> oldValue;
            }
        }
           <span class="hljs-comment">// 若“该key”对应的键值对不存在,则将“key-value”添加到table中 </span>
        modCount++;
        <span class="hljs-comment">//将key-value添加到table[i]处  </span>
        addEntry(hash, key, <span class="hljs-keyword">value</span>, i);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

如果key为null,则将其添加到table[0]对应的链表中,putForNullKey的源码如下:

<code class="hljs cs has-numbering"><span class="hljs-comment">// putForNullKey()的作用是将“key为null”键值对添加到table[0]位置 </span>
    <span class="hljs-keyword">private</span> V <span class="hljs-title">putForNullKey</span>(V <span class="hljs-keyword">value</span>) {
        <span class="hljs-keyword">for</span> (Entry<K,V> e = table[<span class="hljs-number">0</span>]; e != <span class="hljs-keyword">null</span>; e = e.next) {
            <span class="hljs-keyword">if</span> (e.key == <span class="hljs-keyword">null</span>) {
                V oldValue = e.<span class="hljs-keyword">value</span>;
                e.<span class="hljs-keyword">value</span> = <span class="hljs-keyword">value</span>;
                e.recordAccess(<span class="hljs-keyword">this</span>);
                <span class="hljs-keyword">return</span> oldValue;
            }
        }
        modCount++;
         <span class="hljs-comment">// 如果没有存在key为null的键值对,则直接插入到table[0]处</span>
        addEntry(<span class="hljs-number">0</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">value</span>, <span class="hljs-number">0</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }
</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

如果key不为null,则同样先求出key的hash值,根据hash值得出在table中的索引,而后遍历对应的单链表,如果单链表中存在与目标key相等的键值对,则将新的value覆盖旧的value,比将旧的value返回,如果找不到与目标key相等的键值对,或者该单链表为空,则将该键值对插入到改单链表的头结点位置(每次新插入的节点都是放在头结点的位置),该操作是有addEntry方法实现的,它的源码如下:

<code class="hljs cs has-numbering">  <span class="hljs-comment">// 新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。</span>
    <span class="hljs-keyword">void</span> addEntry(<span class="hljs-keyword">int</span> hash, K key, V <span class="hljs-keyword">value</span>, <span class="hljs-keyword">int</span> bucketIndex) {
     <span class="hljs-comment">// 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小   </span>
        <span class="hljs-keyword">if</span> ((size >= threshold) && (<span class="hljs-keyword">null</span> != table[bucketIndex])) {
            resize(<span class="hljs-number">2</span> * table.length);
            hash = (<span class="hljs-keyword">null</span> != key) ? hash(key) : <span class="hljs-number">0</span>;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, <span class="hljs-keyword">value</span>, bucketIndex);
    }

      <span class="hljs-comment">// 创建Entry。将“key-value”插入指定位置。</span>
    <span class="hljs-keyword">void</span> createEntry(<span class="hljs-keyword">int</span> hash, K key, V <span class="hljs-keyword">value</span>, <span class="hljs-keyword">int</span> bucketIndex) {
        <span class="hljs-comment">// 保存“bucketIndex”位置的值到“e”中   </span>
        Entry<K,V> e = table[bucketIndex];
          <span class="hljs-comment">// 设置“bucketIndex”位置的元素为“新Entry”,</span>
        <span class="hljs-comment">// 设置“e”为“新Entry的下一个节点”  </span>
        table[bucketIndex] = <span class="hljs-keyword">new</span> Entry<>(hash, key, <span class="hljs-keyword">value</span>, e);
        size++;
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

需要说明的是,每次加入键值对时,都要判断当前已用的槽的数目是否大于等于阀值(容量*加载因子),如果大于等于,则进行扩容,将容量扩为原来容量的2倍。
注意createEntry倒数第三行的构造方法,将key-value键值对赋给table[bucketIndex],并将其next指向元素e,这便将key-value放到了头结点中,并将之前的头结点接在了它的后面。该方法也说明,每次put键值对的时候,总是将新的该键值对放在table[bucketIndex]处(即头结点处)。

6. 扩容方法

上面我们看到了扩容的方法,resize方法,它的源码如下:

<code class="hljs glsl has-numbering"><span class="hljs-comment">// 重新调整HashMap的大小,newCapacity是调整后的容量</span>
    <span class="hljs-keyword">void</span> resize(<span class="hljs-keyword">int</span> newCapacity) {
        Entry[] oldTable = table;
        <span class="hljs-keyword">int</span> oldCapacity = oldTable.<span class="hljs-built_in">length</span>;
        <span class="hljs-keyword">if</span> (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-comment">// 新建一个HashMap,将“旧HashMap”的全部元素添加到“新HashMap”中,    </span>
    <span class="hljs-comment">// 然后,将“新HashMap”赋值给“旧HashMap”。</span>
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (<span class="hljs-keyword">int</span>)Math.<span class="hljs-built_in">min</span>(newCapacity * loadFactor, MAXIMUM_CAPACITY + <span class="hljs-number">1</span>);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

很明显,是新建了一个HashMap的底层数组,而后调用transfer方法,将就HashMap的全部元素添加到新的HashMap中(要重新计算元素在新的数组中的索引位置)。transfer方法的源码如下:

<code class="hljs java has-numbering">   <span class="hljs-comment">// 将HashMap中的全部元素都添加到newTable中</span>
    <span class="hljs-keyword">void</span> transfer(Entry[] newTable, <span class="hljs-keyword">boolean</span> rehash) {
        <span class="hljs-keyword">int</span> newCapacity = newTable.length;
        <span class="hljs-keyword">for</span> (Entry<K,V> e : table) {
            <span class="hljs-keyword">while</span>(<span class="hljs-keyword">null</span> != e) {
                Entry<K,V> next = e.next;
                <span class="hljs-keyword">if</span> (rehash) {
                    e.hash = <span class="hljs-keyword">null</span> == e.key ? <span class="hljs-number">0</span> : hash(e.key);
                }
                <span class="hljs-keyword">int</span> i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

很明显,扩容是一个相当耗时的操作,因为它需要重新计算这些元素在新的数组中的位置并进行复制处理。因此,我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。

7. containsKey方法和containsValue方法

很明显,扩容是一个相当耗时的操作,因为它需要重新计算这些元素在新的数组中的位置并进行复制处理。因此,我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。

8. 求hash值和索引值的方法

分析下求hash值和索引值的方法,这两个方法便是HashMap设计的最为核心的部分,二者结合能保证哈希表中的元素尽可能均匀地散列。

<code class="hljs javascript has-numbering"> <span class="hljs-comment">//求hash值的方法,重新计算hash值</span>
    final int hash(<span class="hljs-built_in">Object</span> k) {
        int h = hashSeed;
        <span class="hljs-keyword">if</span> (<span class="hljs-number">0</span> != h && k <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">String</span>) {
            <span class="hljs-keyword">return</span> sun.misc.Hashing.stringHash32((<span class="hljs-built_in">String</span>) k);
        }

        h ^= k.hashCode();
        h ^= (h >>> <span class="hljs-number">20</span>) ^ (h >>> <span class="hljs-number">12</span>);
        <span class="hljs-keyword">return</span> h ^ (h >>> <span class="hljs-number">7</span>) ^ (h >>> <span class="hljs-number">4</span>);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

它只是一个数学公式,JDK这样设计对hash值的计算,自然有它的好处,至于为什么这样设计,我们这里不去追究,只要明白一点,用的位的操作使hash值的计算效率很高。

由hash值找到对应索引的方法如下:

<code class="hljs perl has-numbering">// 返回h在数组中的索引值,这里用&代替取模,旨在提升效率   
    // h & (<span class="hljs-keyword">length</span>-<span class="hljs-number">1</span>)保证返回值的小于<span class="hljs-keyword">length</span>  
    static <span class="hljs-keyword">int</span> indexFor(<span class="hljs-keyword">int</span> h, <span class="hljs-keyword">int</span> <span class="hljs-keyword">length</span>) {
        <span class="hljs-regexp">//</span> assert Integer.bitCount(<span class="hljs-keyword">length</span>) == <span class="hljs-number">1</span> : <span class="hljs-string">"length must be a non-zero power of 2"</span>;
        <span class="hljs-keyword">return</span> h & (<span class="hljs-keyword">length</span>-<span class="hljs-number">1</span>);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div>

这个我们要重点说下,我们一般对哈希表的散列很自然地会想到用hash值对length取模(即除法散列法),Hashtable中也是这样实现的,这种方法基本能保证元素在哈希表中散列的比较均匀,但取模会用到除法运算,效率很低,HashMap中则通过h&(length-1)的方法来代替取模,同样实现了均匀的散列,但效率要高很多,这也是HashMap对Hashtable的一个改进。

接下来,我们分析下为什么哈希表的容量一定要是2的整数次幂。首先,length为2的整数次幂的话,
,所以本来应该写成:index = n % length的,变为可以写成:index = n & (length - 1)两者在length为2的幂方时等价。

h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;其次,length为2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性,而如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间,因此,length取2的整数次幂,是为了使不同hash值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值