(转)Java集合框架:HashMap




Java集合框架概述

  Java集合框架无论是在工作、学习、面试中都会经常涉及到,相信各位也并不陌生,其强大也不用多说,博主最近翻阅java集合框架的源码以及搜索一些相关资料整理出Java集合框架的系列。一方面是做一个总结,方便以后查阅,另一方面希望各位小伙伴能够提出不足之处,我会及时更新修改。 
这里写图片描述 
  博主从网上抠了一张图,觉得画得还是比较形象的,给大家参考一下。 
  上述类图中,实线边框的是实现类,比如ArrayList,LinkedList,HashMap等,折线边框的是抽象类,比如AbstractCollection,AbstractList,AbstractMap等,而点线边框的是接口,比如Collection,Iterator,List等。 
  发现一个特点,上述所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含hashNext(), next(), remove()三种方法。它的一个子接口LinkedIterator在它的基础上又添加了三种方法,分别是add(),previous(),hasPrevious()。也就是说如果是先Iterator接口,那么在遍历集合中元素的时候,只能往后遍历,被遍历后的元素不会在遍历到,通常无序集合实现的都是这个接口,比如HashSet,HashMap;而那些元素有序的集合,实现的一般都是LinkedIterator接口,实现这个接口的集合可以双向遍历,既可以通过next()访问下一个元素,又可以通过previous()访问前一个元素,比如ArrayList。 
  本篇首先对HashMap进行详解,后续内容慢慢跟进。


HashMap定义

  如无特殊说明,本文以jdk7为准进行说明。

<code class="hljs php has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">package java.util;
import java.io.*;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">HashMap</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">K</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">V</span>>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">AbstractMap</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">K</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">V</span>>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Map</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">K</span>,<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">V</span>>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Cloneable</span>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Serializable</span>{</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

HashMap概述

  工作原理:通过hash的方法,通过put和get存储和获取对象。存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java 8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。 
  以Entry[]数组实现的哈希桶数组,用Key的哈希值取模桶数组的大小可得到数组下标。

<code class="hljs xml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">static final Entry<span class="php" style="box-sizing: border-box;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;"><?</span>,<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">?></span></span>[] EMPTY_TABLE = {};
transient Entry<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">K,V</span>></span>[] table = (Entry<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">K,V</span>></span>[]) EMPTY_TABLE;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

  基于Map接口实现、允许null键/值、非同步、不保证有序(比如插入的顺序)、也不保证序不随时间变化。HashMap存储着Entry(hash, key, value, next)对象。 
  当key==null时,存在table[0]即第一个桶中,hash值为0。HashMap对key==null的键值对会做单独处理,譬如:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> V <span class="hljs-title" style="box-sizing: border-box;">getForNullKey</span>();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> V <span class="hljs-title" style="box-sizing: border-box;">putForNullKey</span>(V <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

  这两个方法。 
  在HashMap中有两个很重要的参数,容量(Capacity)和负载因子(Load factor)。 
  Capacity的默认值为16: 
  static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
   负载因子的默认值为0.75: 
  static final float DEFAULT_LOAD_FACTOR = 0.75f; 
  简单的说,Capacity就是bucket的大小,Load factor就是bucket填满程度的最大比例。如果对迭代性能要求很高的话不要把Capacity设置过大,也不要把load factor设置过小。当bucket中的entries的数目大于capacity*load factor时就需要调整bucket的大小为当前的2倍。 
  可以设置初始容量Capacity,但是在HashMap处理过程中,是会把Capacity扩充成2的倍数,怎么理解?比如你设置的初始值17,但是17不是2的整数倍,会扩容成32,再比如你初始设置的是15,会扩容成16,具体的实现在下面:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">roundUpToPowerOf2</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> number) {
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// assert number >= 0 : "number must be non-negative";</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) ? Integer.highestOneBit((number - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) << <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) : <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

  HashMap中有一个成员变量modCount,这个用来实现“fast-fail”机制(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其结构性的修改,这是迭代器会立马感知到,并且立刻抛出ConcurrentModificationException异常,而不是等待迭代完成之后才告诉你已经出错。


HashMap的遍历

  举个例子来表述下Map的遍历,具体的可以参考《 Java中如何遍历Map对象

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Map</span><span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>,<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Integer</span><span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">></span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span> <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">new</span> HashMap<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><></span>();
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s1"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s2"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s3"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s4"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s5"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">null</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">9</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s6"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">6</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s7"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">7</span>);
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>put(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"s8"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>);
        for(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>Entry<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;"><</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>,<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Integer</span><span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">></span> entry:<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">map</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>entrySet())
        {
            System<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>out<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>println(entry<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getKey()<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span><span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">":"</span><span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span>entry<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getValue());
        }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul>

  输出结果:

<code class="hljs css has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">null</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:9</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s2</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:2</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s1</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:1</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s7</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:7</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s8</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:8</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s5</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:5</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s6</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:6</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s3</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:3</span>
<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">s4</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:4</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

  存储结构如图(ppt画的图,比较简陋~): 
这里写图片描述

put函数大致的思路为

  1. 先判断table(存放bullet的数组,初始类定义:transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;)是否为空,如果为空则扩充table,其中包括确保table的大小为2的整数倍。
  2. 如果key值为null,则特殊处理,调用putForNullKey(V value),hash值为0,存入table中,返回。
  3. 如果key值不为null,则计算key的hash值;
  4. 然后计算key在table中的索引index;
  5. 遍历table[index]的链表,如果发现链表中有bullet中的键的hash值与key相等,并且调用equals()方法也返回true,则替换旧值(oldValue),保证key的唯一性;
  6. 如果没有,在插入之前先判断table中阈值的大小,如果table中的bullet个数size超过阈值(threshold)则扩容(resize)两倍;注意扩容的顺序,扩容之前old1->old2->old3,扩容之后old3->old2->old1,扩展之前和扩容之后的table的index不一定相同,但是对于原bullet中的链表中的数据在扩容之后肯定还在一个链表中,因为hash值是一样的。
  7. 插入新的bullet。注意插入的顺序,原先table[index]的链表比如 old1->old2->old3,插入新值之后为new1->old1->old2->old3.
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> V <span class="hljs-title" style="box-sizing: border-box;">put</span>(K key, V <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (key == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>)
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> putForNullKey(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> hash = hash(key);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = indexFor(hash, table.length);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<K,V> e = table[i]; e != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>; e = e.next) {
            Object k;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>;
                e.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span> = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>;
                e.recordAccess(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>, i);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> addEntry(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> hash, K key, V <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> bucketIndex) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((size >= threshold) && (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> != table[bucketIndex])) {
            resize(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> * table.length);
            hash = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> != key) ? hash(key) : <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">value</span>, bucketIndex);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>

注:在jdk8中,新增默认为8的閥值(TREEIFY_THRESHOLD),当一个桶里的Entry超过閥值,就不以单向链表而以红黑树来存放以加快Key的查找速度。

get函数的大致思路为

  1. 判断key值是否为null,如果是则特殊处理(在table[0]的链表中寻找)
  2. 否则计算hash值,进而获得table中的index值
  3. 在table[index]的链表中寻找,根据hash值和equals()方法获得相应的value。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> V <span class="hljs-title" style="box-sizing: border-box;">get</span>(Object key) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (key == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>)
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> getForNullKey();
        Entry<K,V> entry = getEntry(key);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> == entry ? <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> : entry.getValue();
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Entry<K,V> getEntry(Object key) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (size == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> hash = (key == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) ? <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> : hash(key);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
             e = e.next) {
            Object k;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (e.hash == hash &&
                ((k = e.key) == key || (key != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> && key.equals(k))))
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> e;
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>

hash和indexFor方法:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> hash(Object k) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> h = hashSeed;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> != h && k <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">instanceof</span> String) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();
        h ^= (h >>> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">20</span>) ^ (h >>> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">12</span>);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> h ^ (h >>> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">7</span>) ^ (h >>> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>);
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">native</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">hashCode</span>();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> indexFor(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> h, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> length) {
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> h & (length-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>

看懂没?还是实地检验下比较形象~ 
譬如key==”key”,那么key的hashCode为(十进制数为106079): 
0000 0000 0000 0001 1001 1110 0101 1111 
执行h ^= (h >>> 20) ^ (h >>> 12);的时候 
h>>>20: 
0000 0000 0000 0000 0000 0000 0000 0000 
h>>>12: 
0000 0000 0000 0000 0000 0000 0001 1001 
h ^= (h >>> 20) ^ (h >>> 12): 
0000 0000 0000 0001 1001 1110 0100 0110 
执行h ^ (h >>> 7) ^ (h >>> 4)的时候 
h >>> 7: 
0000 0000 0000 0000 0000 0011 0011 1100 
h>>>4: 
0000 0000 0000 0000 0001 1001 1110 0100 
h ^ (h >>> 7) ^ (h >>> 4): 
0000 0000 0000 0001 1000 0100 1001 1110(十进制表示为99486) 
假设Capacity是默认16,那么 
h&(length-1): 
0000 0000 0000 0001 1000 0100 1001 1110 & 
0000 0000 0000 0000 0000 0000 0000 1111 = 
0000 0000 0000 0000 0000 0000 0000 1110 = 14 
我们可以通过eclipse的debug工作中的Variables查看相关细节:

这里写图片描述 
   可以看到key=”key”果然在table[14]当中。 
  h & (length-1)这种取模用位运算的方式比较快,这个需要数组的大小永远是2的N次方来保证其有效性。

jdk8中hash函数的实现:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);


使用自定义的对象作为键

  可以使用任何对象作为键,只要它遵守了equals()和hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。 
  如下:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> collections;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">StringOther</span>
{</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> String name;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">StringOther</span>(String name)
    {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.name = name;
    }

    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">hashCode</span>()
    {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> name.hashCode();
    }

    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">equals</span>(Object obj)
    {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(obj == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>)
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(!(obj <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">instanceof</span> StringOther))
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
        StringOther so = (StringOther)obj;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> so.getName().equals(name);
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> String <span class="hljs-title" style="box-sizing: border-box;">getName</span>()
    {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.name;
    }

    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> String <span class="hljs-title" style="box-sizing: border-box;">toString</span>()
    {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"["</span>+<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.name+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">":"</span>+<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.hashCode()+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"]"</span>;
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li></ul>

  测试代码:

<code class="hljs vhdl has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Map</span><StringOther,<span class="hljs-typename" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>> maps = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HashMap<>(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">16</span>);
        StringOther so1 = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> StringOther(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"so1"</span>);
        StringOther so2 = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> StringOther(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"so2"</span>);
        maps.put(so1,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"1"</span>);
        maps.put(so2,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"2"</span>);

        System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(maps);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

  输出结果:{[so1:114005]=1, [so2:114006]=2} 
  注意重写equals方法的时候一定要写成:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">equals</span>(Object obj)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

的形式,注意注解Override和参数类型Object obj,如写成@Override public boolean equals(StringOther obj)这样会出现意想不到的错误,如果对其不解,可在下方留言。 
  并且在每个覆盖了equals方法的类中也必须覆盖hashCode方法,如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运转,这样的集合包括HashMap, HashSet和Hashtable.


序列化

  细心的朋友可能注意到HashMap实现了Serializable接口,但是对table的定义(transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;)却是transient的。然后又自己实现了writeObject()和readObject()两个方法。

假设我们已经知道的事实:声明为transient的变量不再是对象持久化的一部分。如果不清楚java序列化相关细节,可以参考《JAVA序列化》。

  那么这是为什么呢?原因有两点:

  1. HashMap的put与get基于hashCode的实现,而hashCode是native方法,对每一个不同的java环境来说,同一个key所计算的hashCode是不相同的,所以反序列化后table的index会发生变化,无法还原
  2. HashMap默认size到达阈值threshold之后进行扩容,很明显HashMap不可能保证每一个bullet都有数据,很多都为null,如果对这部分数据进行序列化则造成不必要的资源浪费。

  由于Java自身的序列化有明显的缺点:占用空间大以及低效,博主建议采用其他的方法进行序列化,譬如json或者protobuff等,详细可以参考《浅析若干Java序列化工具


与Hashtable的区别

  由于Hashtable逐渐退出历史舞台,故不会另起一篇进行重点解析,只在这里与HashMap区别一下。 
  HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。

  1. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable的key和value都不能为null,否则会报NullPointException)。
  2. HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
  3. 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
  4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
  5. HashMap不能保证随着时间的推移Map中的元素次序是不变的。
  6. Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

  HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap); 
  Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。


HashMap常见问题总结

  1. HashMap的工作原理?(见开篇)
  2. 你知道get和put的原理吗?equals()和hashCode()的都有什么作用? 
    答:通过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而获得buckets的位置。如果产生碰撞,则利用key.equals()方法去链表或树中去查找对应的节点。
  3. 为什么String, Interger这样的wrapper类适合作为键? 
    String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果你可以仅仅通过将某个field声明成final就能保证hashCode是不变的,那么请这么做吧。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。

参考资料: 
1. 《Java HashMap工作原理及实现》 
2. 《关于Java集合的小抄》 
3. 《HashMap和Hashtable的区别》 
4. 《Java 集合类详解》 
5. 《Effective Java(Second Edition)》. Joshua Bloch.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值