天天看點

java HashMap 與HashTable的差別

HashMap 與HashTable的差別

HashMap與Hashtable的差別是面試中經常遇到的一個問題。這個問題看似簡單,但如果深究進去,也能了解到不少知識。本文對兩者從來源,特性,算法等多個方面進行對比總結。力争多角度,全方位的展示二者的不同,做到此問題的終結版。

1 作者

Hashtable的作者:

java HashMap 與HashTable的差別

HashMap的作者:

java HashMap 與HashTable的差別

Hash Map的作者比Hashtable的作者多了著名頂頂的并發大神Doug Lea。他寫了util.concurrent包。著有并發程式設計聖經Concurrent Programming in Java: Design Principles and Patterns 一書。他的個人首頁: http://g.oswego.edu/

Josh Bloch 為上司了衆多Java平台特性的設計和實作,其中包括Java Collection架構、java.math包以及assert機制。著有 Effective Java 一書。

Arthur van Hoff最早任職于矽谷的Sun Microsystems公司,從事Java程式語言的早期開發工作。設計并實作了JDK 1.0的許多方面,包括Java編譯器、Java調試器、許多标準Java類以及HotJava浏覽器。随後創立了多家成功的企業,其中包括Marimba(1999年IPO)、Strangeberry(後被TiVo收購)、ZING(後被Dell收購)和Ellerdale(後被Flipboard收購)。Java命名來源有這麼一種說法,來源于開發人員名字的組合:James Gosling、Arthur Van Hoff和Andy Bechtolsheim首字母的縮寫。

Neal Gafter是Java SE 4和5語言增強的主要設計者和實作者,他的Java閉包實作赢得了OpenJDK創新者挑戰賽的大獎。他也在繼續參與SE 7和8的語言發展。之前Neal在為Google的線上月曆工作,也曾經是C++标準委員會的一員,并曾在Sun微系統公司,MicroTec研究院和德州儀器上司開發C和C++編譯器。如今Neal在微軟開發.NET平台程式設計語言。Neal是《Java Puzzlers:Traps, Pitfalls and Corner Cases》(Addison Wesley,2005)一書的合作者。他擁有羅徹斯特大學計算機科學的博士學位。

可見這些作者都是java乃至整個it領域大名鼎鼎的人物。也隻有這些大師級人物才能寫出HashMap這麼大道至簡的資料類型了。

2 産生時間

Hashtable是java一開始釋出時就提供的鍵值映射的資料結構,而HashMap産生于JDK1.2。雖然Hashtable比HashMap出現的早一些,但是現在Hashtable基本上已經被棄用了。而HashMap已經成為應用最為廣泛的一種資料類型了。造成這樣的原因一方面是因為Hashtable是線程安全的,效率比較低。另一方面可能是因為Hashtable沒有遵循駝峰命名法吧。。。

3 繼承的父類不同

HashMap和Hashtable不僅作者不同,而且連父類也是不一樣的。HashMap是繼承自AbstractMap類,而HashTable是繼承自Dictionary類。不過它們都實作了同時實作了map、Cloneable(可複制)、Serializable(可序列化)這三個接口

java HashMap 與HashTable的差別
java HashMap 與HashTable的差別

Dictionary類是一個已經被廢棄的類(見其源碼中的注釋)。父類都被廢棄,自然而然也沒人用它的子類Hashtable了。

NOTE: This class is obsolete. New implementations should

implement the Map interface, rather than extending this class.

4 對外提供的接口不同

Hashtable比HashMap多提供了elments() 和contains() 兩個方法。

elments() 方法繼承自Hashtable的父類Dictionnary。elements() 方法用于傳回此Hashtable中的value的枚舉。

contains()方法判斷該Hashtable是否包含傳入的value。它的作用與containsValue()一緻。事實上,contansValue() 就隻是調用了一下contains() 方法。

java HashMap 與HashTable的差別

5 對Null key 和Null value的支援不同

Hashtable既不支援Null key也不支援Null value。Hashtable的put()方法的注釋中有說明。

java HashMap 與HashTable的差別

當key為Null時,調用put() 方法,運作到下面這一步就會抛出空指針異常。因為拿一個Null值去調用方法了。

當value為null值時,Hashtable對其做了限制,運作到下面這步也會抛出空指針異常。

java HashMap 與HashTable的差別

HashMap中,null可以作為鍵,這樣的鍵隻有一個;可以有一個或多個鍵所對應的值為null。當get()方法傳回null值時,可能是 HashMap中沒有該鍵,也可能使該鍵所對應的值為null。是以,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。

6 線程安全性不同

Hashtable是線程安全的,它的每個方法中都加入了Synchronize方法。在多線程并發的環境下,可以直接使用Hashtable,不需要自己為它的方法實作同步

HashMap不是線程安全的,在多線程并發的環境下,可能會産生死鎖等問題。具體的原因在下一篇文章中會詳細進行分析。使用HashMap時就必須要自己增加同步處理,

雖然HashMap不是線程安全的,但是它的效率會比Hashtable要好很多。這樣設計是合理的。在我們的日常使用當中,大部分時間是單線程操作的。HashMap把這部分操作解放出來了。當需要多線程操作的時候可以使用線程安全的ConcurrentHashMap。ConcurrentHashMap雖然也是線程安全的,但是它的效率比Hashtable要高好多倍。因為ConcurrentHashMap使用了分段鎖,并不對整個資料進行鎖定。

7 周遊方式的内部實作上不同

Hashtable、HashMap都使用了 Iterator。而由于曆史原因,Hashtable還使用了Enumeration的方式 。

HashMap的Iterator是fail-fast疊代器。當有其它線程改變了HashMap的結構(增加,删除,修改元素),将會抛出ConcurrentModificationException。不過,通過Iterator的remove()方法移除元素則不會抛出ConcurrentModificationException異常。但這并不是一個一定發生的行為,要看JVM。

JDK8之前的版本中,Hashtable是沒有fast-fail機制的。在JDK8及以後的版本中 ,HashTable也是使用fast-fail的, 源碼如下:

java HashMap 與HashTable的差別

modCount的使用類似于并發程式設計中的CAS(Compare and Swap)技術。我們可以看到這個方法中,每次在發生增删改的時候都會出現modCount++的動作。而modcount可以了解為是目前hashtable的狀态。每發生一次操作,狀态就向前走一步。設定這個狀态,主要是由于hashtable等容器類在疊代時,判斷資料是否過時時使用的。盡管hashtable采用了原生的同步鎖來保護資料安全。但是在出現疊代資料的時候,則無法保證邊疊代,邊正确操作。于是使用這個值來标記狀态。一旦在疊代的過程中狀态發生了改變,則會快速抛出一個異常,終止疊代行為。

8 初始容量大小和每次擴充容量大小的不同

Hashtable預設的初始大小為11,之後每次擴充,容量變為原來的2n+1。HashMap預設的初始化大小為16。之後每次擴充,容量變為原來的2倍。

建立時,如果給定了容量初始值,那麼Hashtable會直接使用你給定的大小,而HashMap會将其擴充為2的幂次方大小。也就是說Hashtable會盡量使用素數、奇數。而HashMap則總是使用2的幂作為哈希表的大小。

之是以會有這樣的不同,是因為Hashtable和HashMap設計時的側重點不同。Hashtable的側重點是哈希的結果更加均勻,使得哈希沖突減少。當哈希表的大小為素數時,簡單的取模哈希的結果會更加均勻。而HashMap則更加關注hash的計算效率問題。在取模計算時,如果模數是2的幂,那麼我們可以直接使用位運算來得到結果,效率要大大高于做除法。HashMap為了加快hash的速度,将哈希表的大小固定為了2的幂。當然這引入了哈希分布不均勻的問題,是以HashMap為解決這問題,又對hash算法做了一些改動。這進而導緻了Hashtable和HashMap的計算hash值的方法不同

9 計算hash值的方法不同

為了得到元素的位置,首先需要根據元素的 KEY計算出一個hash值,然後再用這個hash值來計算得到最終的位置。

Hashtable直接使用對象的hashCode。hashCode是JDK根據對象的位址或者字元串或者數字算出來的int類型的數值。然後再使用除留餘數發來獲得最終的位置。

java HashMap 與HashTable的差別

Hashtable在計算元素的位置時需要進行一次除法運算,而除法運算是比較耗時的。

HashMap為了提高計算效率,将哈希表的大小固定為了2的幂,這樣在取模預算時,不需要做除法,隻需要做位運算。位運算比除法的效率要高很多。

HashMap的效率雖然提高了,但是hash沖突卻也增加了。因為它得出的hash值的低位相同的機率比較高,而計算位運算

為了解決這個問題,HashMap重新根據hashcode計算hash值後,又對hash值做了一些運算來打散資料。使得取得的位置更加分散,進而減少了hash沖突。當然了,為了高效,HashMap隻做了一些簡單的位處理。進而不至于把使用2 的幂次方帶來的效率提升給抵消掉。

java HashMap 與HashTable的差別

參考linkedhashmap: https://blog.csdn.net/a724888/article/details/80290276