java数据结构-HashMap

<a target=_blank id="viewpost1_TitleUrl" href="http://www.blogjava.net/dongbule/archive/2011/02/15/344387.html">java数据结构-HashMap</a>	 <div class="postText">一直以来似乎都有一个错觉,认为map跟其他的集合类一样继承自Collection,其实不然,Map和Collection在结构层次上是没有任何关系的,通过查看源码可以发现map所有操作都是基于key-value对,而不是单独的元素。

 下面以HashMap为例子,深入对Map的实现机制进行了解,在这个过程中,请打开jdk源码。

<strong>Hash算法</strong>

HashMap使用Hash算法,所以在解剖HashMap之间,需要先简单的了解Hash算法,Hash算法一般也成为散列算法,通过散列算法将任意的值转化成固定的长度输出,该输出就是散列值,这是一种压缩映射,也就是,散列值的空间远远小于输入的值空间。
 简单的说,hash算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系,(每一个真实值只能有一个键值,但是一个键值可以对应多个真实值),这样可以快速在数组等里面存取数据。

 下面我们建立一个HashMap,然后往里面放入12对key-value,这个HashMap的默认数组长度为16,我们的key分别存放在该数组的格子中,每个格子下面存放的元素又是以链表的方式存放元素。

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--
   <span style="color: rgb(0, 0, 255);">public<span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> main(String[] args) {
         Map map </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> HashMap();
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">What</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">You</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Don't</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Know</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">About</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Geo</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">APIs</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Can't</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Hurt</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">you</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">google</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">map</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
         map.put(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">hello</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">chenyz</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);
     }</span>
当我们新添加一个元素时,首先我们通过Hash算法计算出这个元素的Hash值的hashcode,通过这个hashcode的值,我们就可以计算出这个新元素应该存放在这个hash表的哪个格子里面,如果这个格子中已经存在元素,那么就把新的元素加入到已经存在格子元素的链表中。

 运行上面的程序,我们对HashMap源码进行一点修改,打印出每个key对象的hash值

What-->hash值:8
 You-->hash值:3
 Don't-->hash值:7
 Know-->hash值:13
 About-->hash值:11
 Geo-->hash值:12
 APIs-->hash值:1
 Can't-->hash值:7
 Hurt-->hash值:1
 you-->hash值:10
 google-->hash值:3
 map-->hash值:8
 hello-->hash值:0

计算出来的Hash值分别代表该key应该存放在Hash表中对应数字的格子中,如果该格子已经有元素存在,那么该key就以链表的方式依次放入格子中

<img width="616" height="193" alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/jar/hashmap1.png" />

 从上表可以看出,Hash表是线性表和链表的综合所得,根据数据结构的定义,可以得出粗劣的结论,Hash算法的存取速度要比数组差一些,但是比起单纯的链表,在查找和存取方面却要好多。

 如果要查找一个元素时,同样的方式,通过Hash函数计算出这个元素的Hash值hashcode,然后通过这个hashcode值,直接找到跟这个hash值相对应的线性格子,进如该格子后,对这个格子存放的链表元素逐个进行比较,直到找到对应的hash值。

 在简单了解完Hash算法后,我们打开HashMap源码

<strong>初始化HashMap</strong>

下面我们看看Map map = new HashMap();这段代码究竟做了什么,发生了什么数据结构的变化。

HashMap中几个重要的属性

transient Entry[] table;
用来保存key-value的对象Entry数组,也就是Hash表

transient int size;
返回HashMap的键值对个数

final float loadFactor;
负载因子,用来决定Entry数组是否扩容的因子,HashMap默认是0.75f

 int threshold;
重构因子,(capacity * load factor)负载因子与Entry[]数组容积的乘值

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> HashMap</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">
     </span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);"> AbstractMap</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">
     </span><span style="color: rgb(0, 0, 255);">implements</span><span style="color: rgb(0, 0, 0);"> Map</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">, Cloneable, Serializable
 {
     </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> threshold;    

     </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">float</span><span style="color: rgb(0, 0, 0);"> loadFactor;

     </span><span style="color: rgb(0, 0, 255);">transient</span><span style="color: rgb(0, 0, 0);"> Entry[] table;

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">float</span><span style="color: rgb(0, 0, 0);"> DEFAULT_LOAD_FACTOR </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0.75f</span><span style="color: rgb(0, 0, 0);">;

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> DEFAULT_INITIAL_CAPACITY </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">16</span><span style="color: rgb(0, 0, 0);">;

     </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> HashMap(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> initialCapacity, </span><span style="color: rgb(0, 0, 255);">float</span><span style="color: rgb(0, 0, 0);"> loadFactor) {
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (initialCapacity </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">)
             </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> IllegalArgumentException(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Illegal initial capacity: </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">
                                                initialCapacity);
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (initialCapacity </span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY)
             initialCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY;
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (loadFactor </span><span style="color: rgb(0, 0, 0);"><=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);"> Float.isNaN(loadFactor))
             </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> IllegalArgumentException(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Illegal load factor: </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">
                                                loadFactor);

         </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);"> Find a power of 2 >= initialCapacity</span><span style="color: rgb(0, 128, 0);">
</span><span style="color: rgb(0, 0, 0);">        </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> capacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">;
         </span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);"> (capacity </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> initialCapacity)
             capacity </span><span style="color: rgb(0, 0, 0);"><<=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">;

         </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.loadFactor </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> loadFactor;
         threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(capacity </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> loadFactor);
         table </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry[capacity];
         init();
     }</span></div>以public HashMap(int initialCapacity, float loadFactor)构造函数为例,另外两个构造函数实际上也是以同种方式来构建HashMap.

首先是要确定hashMap的初始化的长度,这里使用的策略是循环查出一个大于initialCapacity的2的次方的数,例如initialCapacity的值是10,那么大于10的数是2的4次方,也就是16,capacity的值被赋予了16,那么实际上table数组的长度是16,之所以采用这样的策略来构建Hash表的长度,是因为2的次方运算对于计算机来说是有相当的效率。

loadFactor,被称为负载因子,HashMap的默认负载因子是0.75f

 threshold,接下来是重构因子,由负载因子和容量的乘机组成,它表示当HashMap元素被存放了多少个之后,需要对HashMap进行重构。

 通过这一系列的计算和定义后,初始化Entry[] table;

<strong>put(key,value)</strong>

接下来看一对key-value是如何被存放到HashMap中:put(key,value)

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> V put(K key, V value) {
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (key </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> putForNullKey(value);
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> hash(key.hashCode());
         
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> i </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> indexFor(hash, table.length);
         System.out.println(key</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">-->hash值:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">i);</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">这就是刚才程序打印出来的key对应hash值</span><span style="color: rgb(0, 128, 0);">
</span><span style="color: rgb(0, 0, 0);">        </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);"> (Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table[i]; e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">; e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next) {
             Object k;
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e.hash </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">&&</span><span style="color: rgb(0, 0, 0);"> ((k </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.key) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> key </span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);"> key.equals(k))) {
                 V oldValue </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.value;
                 e.value </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> value;
                 e.recordAccess(</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">);
                 </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> oldValue;
             }
         }

         modCount</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">;
         addEntry(hash, key, value, i);
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
     }

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> h) {
         h </span><span style="color: rgb(0, 0, 0);">^=</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">12</span><span style="color: rgb(0, 0, 0);">);
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> h </span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">7</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);"> (h </span><span style="color: rgb(0, 0, 0);">>>></span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">4</span><span style="color: rgb(0, 0, 0);">);
     }

     </span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> indexFor(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> h, </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> length) {
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> h </span><span style="color: rgb(0, 0, 0);">&</span><span style="color: rgb(0, 0, 0);"> (length</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">);
     }</span></div>
这里是整个hash的关键,请打开源码查看一步一步查看。

<u>hash(key.hashCode())</u> 计算出key的hash码 //对于hash()的算法,这里有一篇分析很透彻的文章<<a target=_blank href="http://www.javaeye.com/topic/709945"></a><a target=_blank href="http://www.javaeye.com/topic/709945"></a><a target=_blank href="http://www.javaeye.com/topic/709945">HashMap hash方法分析</a>>
<u>indexFor(hash, table.length)</u> 通过一个与算法计算出来,该key应在存放在Hash表的哪个格子中。
<u>for (Entry<K,V> e = table[i]; e != null; e = e.next)</u> 然后再遍历table[i]格中的链表,判断是否已经存在一样的key,如果存在一样的key值,那么就用新的value覆盖旧的value,并把旧的value值返回。
<u>addEntry(hash, key, value, i)</u> 如果经过遍历链表没有发现同样的key,那么进行addEntry函数的操作,增加当前key到hash表中的第i个格子中的链表中

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> addEntry(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash, K key, V value, </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> bucketIndex) {
         Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table[bucketIndex];
         table[bucketIndex] </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);">(hash, key, value, e);
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (size</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">>=</span><span style="color: rgb(0, 0, 0);"> threshold)
             resize(</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> table.length);
     }</span></div><u>
 Entry<K,V> e = table[bucketIndex];</u>  创建一个Entry对象来存放键值(ps:Entry对象是一个链表对象)
<u>table[bucketIndex] = new Entry<K,V>(hash, key, value, e);</u> 将Entry对象添加到链表中
<u>if (size++ >= threshold) resize(2 * table.length);</u> 最后将size进行自增,判断size值是否大于重构因子,如果大于那么就是用resize进行扩容重构。

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> resize(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> newCapacity) {
         Entry[] oldTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> oldCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> oldTable.length;
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (oldCapacity </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY) {
             threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Integer.MAX_VALUE;
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">;
         }

         Entry[] newTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry[newCapacity];
         transfer(newTable);
         table </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable;
         threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(newCapacity </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> loadFactor);
     }</span></div>
这里为什么是否需要扩容重构,其实是涉及到负载因子的性能问题

<strong>loadFactor负载因子</strong>
 上面说过loadFactor是一个hashMap的决定性属性,HashSet和HashMap的默认负载因子都是0.75,它表示,如果哈希表的容量超过3/4时,将自动成倍的增加哈希表的容量,这个值是权衡了时间和空间的成本,如果负载因子较高,虽然会减少对内存空间的需求,但也会增加查找数据的时间开销,无论是put()和get()都涉及到对数据进行查找的动作,所以负载因子是不适宜设置过高

<img width="496" height="250" alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/jar/hashmap2.png" />

<strong>get(key)</strong>

接下来看看get(key)做了什么

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> V get(Object key) {
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (key </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> getForNullKey();
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> hash(key.hashCode());
         </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);"> (Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table[indexFor(hash, table.length)];
              e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
              e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next) {
             Object k;
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e.hash </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> hash </span><span style="color: rgb(0, 0, 0);">&&</span><span style="color: rgb(0, 0, 0);"> ((k </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.key) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> key </span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);"> key.equals(k)))
                 </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> e.value;
         }
         </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
     }</span></div>
这些动作似乎是跟put(key,value)相识,通过hash算法获取key的hash码,再通过indexFor定位出该key存在于table的哪一个下表,获取该下标然后对下标中的链表进行遍历比对,如果有符合就直接返回该key的value值。

<strong>keySet()</strong>

这里还涉及另一个问题,上面说了HashMap是跟set没有任何亲属关系,但map也一样实现了keySet接口,下面谱析一下keySet在hashMap中是如何实现的,这里给出部分代码,请结合源码查看

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 255);"> public</span><span style="color: rgb(0, 0, 0);"> K next() {
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> nextEntry().getKey();
         }

     </span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);"> Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> nextEntry() {
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (modCount </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> expectedModCount)
                 </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> ConcurrentModificationException();
             Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> next;
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
                 </span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> NoSuchElementException();

             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> ((next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">) {
                 Entry[] t </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
                 </span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);"> (index </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> t.length </span><span style="color: rgb(0, 0, 0);">&&</span><span style="color: rgb(0, 0, 0);"> (next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> t[index</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">]) </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)
                     ;
             }
         current </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e;
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> e;
         }</span></div>
代码很简单,就是对每个格子里面的链表进行遍历,也正是这个原因,当我们依次将key值put进hashMap中,但在使用map.entrySet().iterator()进行遍历时候却不是put时候的顺序。

<strong>扩容</strong>
 在前面说到put函数的时候,已经提过了扩容的问题

<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (size</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">>=</span><span style="color: rgb(0, 0, 0);"> threshold)
 resize(</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> table.length);</span></div>

这里一个是否扩容的判断,当数据达到了threshold所谓的重构因子,而不是HashMap的最大容量,就进行扩容。
<div style="padding: 4px 5px 4px 4px; border: 1px solid rgb(204, 204, 204); width: 98%; font-size: 13px; background-color: rgb(238, 238, 238);"><!--



Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/



--><span style="color: rgb(0, 0, 0);">
    </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> resize(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> newCapacity) {
         Entry[] oldTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> oldCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> oldTable.length;
         </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (oldCapacity </span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);"> MAXIMUM_CAPACITY) {
             threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> Integer.MAX_VALUE;
             </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">;
         }

         Entry[] newTable </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> Entry[newCapacity];
         transfer(newTable);
         table </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable;
         threshold </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(newCapacity </span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);"> loadFactor);
     }

     </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> transfer(Entry[] newTable) {
         Entry[] src </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> table;
         </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> newCapacity </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable.length;
         </span><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> j </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">; j </span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);"> src.length; j</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">) {
             Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> src[j];
             </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">) {
                 src[j] </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;
                 </span><span style="color: rgb(0, 0, 255);">do</span><span style="color: rgb(0, 0, 0);"> {
                     Entry</span><span style="color: rgb(0, 0, 0);"><</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">></span><span style="color: rgb(0, 0, 0);"> next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e.next;
                     </span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> i </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> indexFor(e.hash, newCapacity);
                     e.next </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> newTable[i];
                     newTable[i] </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> e;
                     e </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> next;
                 } </span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);"> (e </span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">);
             }
         }
     }</span></div>

 transfer方法实际上是将所有的元素重新进行一些hash,这是因为容量变化了,每个元素相对应的hash值也会不一样。

<strong>使用HashMap</strong>

 1.不要再高并发中使用HashMap,HashMap是线程不安全,如果被多个线程共享之后,将可能发生不可预知的问题。
2.如果数据大小事固定的,最好在初始化的时候就给HashMap一个合理的容量值,如果使用new HashMap()默认构造函数,重构因子的值是16*0.75=12,当HashMap的容量超过了12后,就会进行一系列的扩容运算,重建一个原来成倍的数组,并且对原来存在的元素进行重新的hash运算,如果你的数据是有成千上万的,那么你的成千上万的数据也要跟这你的扩容不断的hash,这将产生高额的内存和cpu的大量开销。

 当然啦,HashMap的函数还有很多,不过都是基于table的链表进行操作,当然也就是hash算法,Map & hashMap在平时我们的应用非常多,最重要的是我们要对每句代码中每块数据结构变化心中有数。



 上面主要是参考了jdk源码,数据结构和一些相关资料本着好记性不如烂博客的精神记录下来,希望朋友们如果发觉哪里不对请指出来,虚心请教
</div></div>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值