HashMap基於hashing原理,咱們經過put()和get()方法儲存和獲取對象。當咱們將鍵值對傳遞給put()方法時,它調用鍵對象的hashCode()方法來計算hashcode,讓後找到bucket位置來儲存值對象。當獲取對象時,經過鍵對象的equals()方法找到正確的鍵值對,而後返回值對象。HashMap使用鏈表來解決碰撞問題,當發生碰撞了,對象將會儲存在鏈表的下一個節點中。 HashMap在每一個鏈表節點中儲存鍵值對對象。html
當兩個不一樣的鍵對象的hashcode相同時會發生什麼? 它們會儲存在同一個bucket位置的鏈表中。鍵對象的equals()方法用來找到鍵值對。java
由於HashMap的好處很是多,我曾經在電子商務的應用中使用HashMap做爲緩存。由於金融領域很是多的運用Java,也出於性能的考慮,咱們會常常用到HashMap和ConcurrentHashMap。git
HashMap和Hashtable都實現了Map接口,但決定用哪個以前先要弄清楚它們之間的分別。主要的區別有:線程安全性,同步(synchronization),以及速度。程序員
1) sychronized意味着在一次僅有一個線程可以更改Hashtable。就是說任何線程要更新Hashtable時要首先得到同步鎖,其它線程要等到同步鎖被釋放以後才能再次得到同步鎖更新Hashtable。github
2) Fail-safe和iterator迭代器相關。若是某個集合對象建立了Iterator或者ListIterator,而後其它的線程試圖「結構上」更改集合對象,將會拋出ConcurrentModificationException異常。但其它線程能夠經過set()方法更改集合對象是容許的,由於這並無從「結構上」更改集合。可是假如已經從結構上進行了更改,再調用set()方法,將會拋出IllegalArgumentException異常。面試
3) 結構上的更改指的是刪除或者插入一個元素,這樣會影響到map的結構。算法
HashMap能夠經過下面的語句進行同步:
Map m = Collections.synchronizeMap(hashMap);編程
Hashtable和HashMap有幾個主要的不一樣:線程安全以及速度。僅在你須要徹底的線程安全的時候使用Hashtable,而若是你使用Java 5或以上的話,請使用ConcurrentHashMap吧。數組
ashMap和HashSet的區別是Java面試中最常被問到的問題。若是沒有涉及到Collection框架以及多線程的面試,能夠說是不完整。而Collection框架的問題不涉及到HashSet和HashMap,也能夠說是不完整。HashMap和HashSet都是collection框架的一部分,它們讓咱們可以使用對象的集合。collection框架有本身的接口和實現,主要分爲Set接口,List接口和Queue接口。它們有各自的特色,Set的集合裏不容許對象有重複的值,List容許有重複,它對集合中的對象進行索引,Queue的工做原理是FCFS算法(First Come, First Serve)。緩存
首先讓咱們來看看什麼是HashMap和HashSet,而後再來比較它們之間的分別。
HashMap和HashSet的區別是Java面試中最常被問到的問題。若是沒有涉及到Collection框架以及多線程的面試,能夠說是不完整。而Collection框架的問題不涉及到HashSet和HashMap,也能夠說是不完整。HashMap和HashSet都是collection框架的一部分,它們讓咱們可以使用對象的集合。collection框架有本身的接口和實現,主要分爲Set接口,List接口和Queue接口。它們有各自的特色,Set的集合裏不容許對象有重複的值,List容許有重複,它對集合中的對象進行索引,Queue的工做原理是FCFS算法(First Come, First Serve)。
首先讓咱們來看看什麼是HashMap和HashSet,而後再來比較它們之間的分別。
HashSet實現了Set接口,它不容許集合中有重複的值,當咱們提到HashSet時,第一件事情就是在將對象存儲在HashSet以前,要先確保對象重寫equals()和hashCode()方法,這樣才能比較對象的值是否相等,以確保set中沒有儲存相等的對象。若是咱們沒有重寫這兩個方法,將會使用這個方法的默認實現。
public boolean add(Object o)方法用來在Set中添加元素,當元素值重複時則會當即返回false,若是成功添加的話會返回true。
HashMap實現了Map接口,Map接口對鍵值對進行映射。Map中不容許重複的鍵。Map接口有兩個基本的實現,HashMap和TreeMap。TreeMap保存了對象的排列次序,而HashMap則不能。HashMap容許鍵和值爲null。HashMap是非synchronized的,但collection框架提供方法能保證HashMap synchronized,這樣多個線程同時訪問HashMap時,能保證只有一個線程更改Map。
public Object put(Object Key,Object value)方法用來將元素添加到map中。
*HashMap* | *HashSet* |
HashMap實現了Map接口 | HashSet實現了Set接口 |
HashMap儲存鍵值對 | HashSet僅僅存儲對象 |
使用put()方法將元素放入map中 | 使用add()方法將元素放入set中 |
HashMap中使用鍵對象來計算hashcode值 | HashSet使用成員對象來計算hashcode值,對於兩個對象來講hashcode可能相同,因此equals()方法用來判斷對象的相等性,若是兩個對象不一樣的話,那麼返回false |
HashMap比較快,由於是使用惟一的鍵來獲取對象 | HashSet較HashMap來講比較慢 |
HashMap的工做原理是近年來常見的Java面試題。幾乎每一個Java程序員都知道HashMap,都知道哪裏要用HashMap,知道Hashtable和HashMap之間的區別,那麼爲什麼這道面試題如此特殊呢?是由於這道題考察的深度很深。這題常常出如今高級或中高級面試中。投資銀行更喜歡問這個問題,甚至會要求你實現HashMap來考察你的編程能力。ConcurrentHashMap和其它同步集合的引入讓這道題變得更加複雜。讓咱們開始探索的旅程吧!
「你用過HashMap嗎?」 「什麼是HashMap?你爲何用到它?」
幾乎每一個人都會回答「是的」,而後回答HashMap的一些特性,譬如HashMap能夠接受null鍵值和值,而Hashtable則不能;HashMap是非synchronized;HashMap很快;以及HashMap儲存的是鍵值對等等。這顯示出你已經用過HashMap,並且對它至關的熟悉。可是面試官來個急轉直下,今後刻開始問出一些刁鑽的問題,關於HashMap的更多基礎的細節。面試官可能會問出下面的問題:
「你知道HashMap的工做原理嗎?」 「你知道HashMap的get()方法的工做原理嗎?」
你也許會回答「我沒有詳查標準的Java API,你能夠看看Java源代碼或者Open JDK。」「我能夠用Google找到答案。」
但一些面試者可能能夠給出答案,「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的問題:
我我的很喜歡這個問題,由於這個問題的深度和廣度,也不直接的涉及到不一樣的概念。讓咱們再來看看這些問題設計哪些知識點:
轉自:原文連接: Javarevisited 翻譯: ImportNew.com - 唐小娟
譯文連接: http://www.importnew.com/7099.html