(轉)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.

發佈了23 篇原創文章 · 獲贊 63 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章