HashMap工做原理(轉載)

轉載自:http://www.importnew.com/7099.html 
html

HashMap的工做原理是近年來常見的Java面試題。幾乎每一個Java程序員都知道HashMap,都知道哪裏要用HashMap,知道Hashtable和HashMap之間的區別, 那麼爲什麼這道面試題如此特殊呢?是由於這道題考察的深度很深。這題常常出如今高級或中高級面試中。投資銀行更喜歡問這個問題,甚至會要求你實現 HashMap來考察你的編程能力。ConcurrentHashMap和其它同步集合的引入讓這道題變得更加複雜。讓咱們開始探索的旅程吧!java

先來些簡單的問題

「你用過HashMap嗎?」 「什麼是HashMap?你爲何用到它?」node

幾乎每一個人都會回答「是的」,而後回答HashMap的一些特性,譬如HashMap能夠接受null鍵值和值,而Hashtable則不 能;HashMap是非synchronized;HashMap很快;以及HashMap儲存的是鍵值對等等。這顯示出你已經用過HashMap,並且 對它至關的熟悉。可是面試官來個急轉直下,今後刻開始問出一些刁鑽的問題,關於HashMap的更多基礎的細節。面試官可能會問出下面的問題:git

「你知道HashMap的工做原理嗎?」 「你知道HashMap的get()方法的工做原理嗎?」程序員

你也許會回答「我沒有詳查標準的Java API,你能夠看看Java源代碼或者Open JDK。」「我能夠用Google找到答案。」github

但一些面試者可能能夠給出答案,「HashMap是基於hashing的原理,咱們使用put(key, value)存儲對象到HashMap中,使用get(key)從HashMap中獲取對象。當咱們給put()方法傳遞鍵和值時,咱們先對鍵調用 hashCode()方法,返回的hashCode用於找到bucket位置來儲存Entry對象。」這裏關鍵點在於指出,HashMap是在 bucket中儲存鍵對象和值對象,做爲Map.Entry。這一點有助於理解獲取對象的邏輯。若是你沒有意識到這一點,或者錯誤的認爲僅僅只在 bucket中存儲值的話,你將不會回答如何從HashMap中獲取對象的邏輯。這個答案至關的正確,也顯示出面試者確實知道hashing以及 HashMap的工做原理。可是這僅僅是故事的開始,當面試官加入一些Java程序員天天要碰到的實際場景的時候,錯誤的答案頻現。下個問題多是關於 HashMap中的碰撞探測(collision detection)以及碰撞的解決方法:面試

「當兩個對象的hashcode相同會發生什麼?」 從這裏開始,真正的困惑開始了,一些面試者會回答由於hashcode相同,因此兩個對象是相等的,HashMap將會拋出異常,或者不會存儲它們。而後 面試官可能會提醒他們有equals()和hashCode()兩個方法,並告訴他們兩個對象就算hashcode相同,可是它們可能並不相等。一些面試 者可能就此放棄,而另一些還能繼續挺進,他們回答「由於hashcode相同,因此它們的bucket位置相同,‘碰撞’會發生。由於HashMap使 用鏈表存儲對象,這個Entry(包含有鍵值對的Map.Entry對象)會存儲在鏈表中。」這個答案很是的合理,雖然有不少種處理碰撞的方法,這種方法 是最簡單的,也正是HashMap的處理方法。但故事尚未完結,面試官會繼續問:編程

「若是兩個鍵的hashcode相同,你如何獲取值對象?」 面試者會回答:當咱們調用get()方法,HashMap會使用鍵對象的hashcode找到bucket位置,而後獲取值對象。面試官提醒他若是有兩個 值對象儲存在同一個bucket,他給出答案:將會遍歷鏈表直到找到值對象。面試官會問由於你並無值對象去比較,你是如何肯定肯定找到值對象的?除非面 試者直到HashMap在鏈表中存儲的是鍵值對,不然他們不可能回答出這一題。數組

其中一些記得這個重要知識點的面試者會說,找到bucket位置以後,會調用keys.equals()方法去找到鏈表中正確的節點,最終找到要找的值對象。完美的答案!緩存

許多狀況下,面試者會在這個環節中出錯,由於他們混淆了hashCode()和equals()方法。由於在此以前hashCode()屢屢出現, 而equals()方法僅僅在獲取值對象的時候纔出現。一些優秀的開發者會指出使用不可變的、聲明做final的對象,而且採用合適的equals()和 hashCode()方法的話,將會減小碰撞的發生,提升效率。不可變性使得可以緩存不一樣鍵的hashcode,這將提升整個獲取對象的速度,使用 String,Interger這樣的wrapper類做爲鍵是很是好的選擇。

若是你認爲到這裏已經完結了,那麼聽到下面這個問題的時候,你會大吃一驚。「若是HashMap的大小超過了負載因子(load factor)定義的容量,怎麼辦?」除 非你真正知道HashMap的工做原理,不然你將回答不出這道題。默認的負載因子大小爲0.75,也就是說,當一個map填滿了75%的bucket時 候,和其它集合類(如ArrayList等)同樣,將會建立原來HashMap大小的兩倍的bucket數組,來從新調整map的大小,並將原來的對象放 入新的bucket數組中。這個過程叫做rehashing,由於它調用hash方法找到新的bucket位置。

若是你可以回答這道問題,下面的問題來了:「你瞭解從新調整HashMap大小存在什麼問題嗎?」你可能回答不上來,這時面試官會提醒你當多線程的狀況下,可能產生條件競爭(race condition)。

當從新調整HashMap大小的時候,確實存在條件競爭,由於若是兩個線程都發現HashMap須要從新調整大小了,它們會同時試着調整大小。在調 整大小的過程當中,存儲在鏈表中的元素的次序會反過來,由於移動到新的bucket位置的時候,HashMap並不會將元素放在鏈表的尾部,而是放在頭部, 這是爲了不尾部遍歷(tail traversing)。若是條件競爭發生了,那麼就死循環了。這個時候,你能夠質問面試官,爲何這麼奇怪,要在多線程的環境下使用HashMap 呢?:)

熱心的讀者貢獻了更多的關於HashMap的問題:

  1. 爲何String, Interger這樣的wrapper類適合做爲鍵? String, Interger這樣的wrapper類做爲HashMap的鍵是再適合不過了,並且String最爲經常使用。由於String是不可變的,也是final 的,並且已經重寫了equals()和hashCode()方法了。其餘的wrapper類也有這個特色。不可變性是必要的,由於爲了要計算 hashCode(),就要防止鍵值改變,若是鍵值在放入時和獲取時返回不一樣的hashcode的話,那麼就不能從HashMap中找到你想要的對象。不 可變性還有其餘的優勢如線程安全。若是你能夠僅僅經過將某個field聲明成final就能保證hashCode是不變的,那麼請這麼作吧。由於獲取對象 的時候要用到equals()和hashCode()方法,那麼鍵對象正確的重寫這兩個方法是很是重要的。若是兩個不相等的對象返回不一樣的 hashcode的話,那麼碰撞的概率就會小些,這樣就能提升HashMap的性能。
  2. 咱們可使用自定義的對象做爲鍵嗎? 這是前一個問題的延伸。固然你可能使用任何對象做爲鍵,只要它遵照了equals()和hashCode()方法的定義規則,而且當對象插入到Map中之 後將不會再改變了。若是這個自定義對象時不可變的,那麼它已經知足了做爲鍵的條件,由於當它建立以後就已經不能改變了。
  3. 咱們可使用CocurrentHashMap來代替Hashtable嗎?這是另一個很熱門的面試題,因 爲ConcurrentHashMap愈來愈多人用了。咱們知道Hashtable是synchronized的,可是 ConcurrentHashMap同步性能更好,由於它僅僅根據同步級別對map的一部分進行上鎖。ConcurrentHashMap固然能夠代替 HashTable,可是HashTable提供更強的線程安全性。看看這篇博客查看Hashtable和ConcurrentHashMap的區別。

我我的很喜歡這個問題,由於這個問題的深度和廣度,也不直接的涉及到不一樣的概念。讓咱們再來看看這些問題設計哪些知識點:

  • hashing的概念
  • HashMap中解決碰撞的方法
  • equals()和hashCode()的應用,以及它們在HashMap中的重要性
  • 不可變對象的好處
  • HashMap多線程的條件競爭
  • 從新調整HashMap的大小

總結

HashMap的工做原理

HashMap基於hashing原理,咱們經過put()和get()方法儲存和獲取對象。當咱們將鍵值對傳遞給put()方法時,它調用鍵對象 的hashCode()方法來計算hashcode,讓後找到bucket位置來儲存值對象。當獲取對象時,經過鍵對象的equals()方法找到正確的 鍵值對,而後返回值對象。HashMap使用鏈表來解決碰撞問題,當發生碰撞了,對象將會儲存在鏈表的下一個節點中。 HashMap在每一個鏈表節點中儲存鍵值對對象。

當兩個不一樣的鍵對象的hashcode相同時會發生什麼? 它們會儲存在同一個bucket位置的鏈表中。鍵對象的equals()方法用來找到鍵值對。

由於HashMap的好處很是多,我曾經在電子商務的應用中使用HashMap做爲緩存。由於金融領域很是多的運用Java,也出於性能的考慮,咱們會常常用到HashMap和ConcurrentHashMap。

 

 

 

What will happen if two different HashMap  key objects have same hashcode?
They will be stored in same bucket but no next node of linked list. And keys equals () method will be used to identify correct key value pair in HashMap .


How null key is handled in HashMap? Since equals() and hashCode() are used to store and retrieve values, how does it work in case of null key?
Null key is handled specially in HashMap, there are two separate method for that putForNullKey(V value) and getForNullKey(). Later is offloaded version of get() to look up null keys.  Null keys always map to index 0.  This null case is split out into separate methods for the sake of performance in the two most commonly used operations (get and put), but incorporated with conditionals in others. In short, equals() and hashcode() method are not used in case of null keys in HashMap.

here is how nulls are retreived from HashMap

    private V getForNullKey () {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

Read more: http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-java.html#ixzz3PPizhRjZ
相關文章
相關標籤/搜索