java HashMap 與HashTable的區別

HashMap 與HashTable的區別

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

1 作者
Hashtable的作者:
在這裏插入圖片描述
HashMap的作者:
在這裏插入圖片描述

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(可序列化)這三個接口
在這裏插入圖片描述
在這裏插入圖片描述

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() 方法。

在這裏插入圖片描述

5 對Null key 和Null value的支持不同
Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的註釋中有說明。
在這裏插入圖片描述

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

當value爲null值時,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的, 源碼如下:
在這裏插入圖片描述

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類型的數值。然後再使用除留餘數發來獲得最終的位置。
在這裏插入圖片描述

Hashtable在計算元素的位置時需要進行一次除法運算,而除法運算是比較耗時的。
HashMap爲了提高計算效率,將哈希表的大小固定爲了2的冪,這樣在取模預算時,不需要做除法,只需要做位運算。位運算比除法的效率要高很多。

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

爲了解決這個問題,HashMap重新根據hashcode計算hash值後,又對hash值做了一些運算來打散數據。使得取得的位置更加分散,從而減少了hash衝突。當然了,爲了高效,HashMap只做了一些簡單的位處理。從而不至於把使用2 的冪次方帶來的效率提升給抵消掉。
在這裏插入圖片描述

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章