HashMap和Hashtable的區別
1.底層結構不同
1.1繼承體系的區別
1.2內部實現使用的數組初始化和擴容方式不同
Hashtable:
(1) Hashtable繼承於Dictionary類,實現了Map接口。Map是"key-value鍵值對"接口,Dictionary是聲明瞭操作"鍵值對"函數接口的抽象類。
(2) Hashtable是通過"拉鍊法"實現的哈希表。它包括幾個重要的成員變量:table, count, threshold, loadFactor, modCount。
table是一個Entry[]數組類型,而Entry實際上就是一個單向鏈表。哈希表的"key-value鍵值對"都是存儲在Entry數組中的。
count是Hashtable的大小,它是Hashtable保存的鍵值對的數量。
threshold是Hashtable的閾值,用於判斷是否需要調整Hashtable的容量。threshold的值="容量*加載因子"。
loadFactor就是加載因子。
modCount是用來實現fail-fast機制的
HashMap:
(1) HashMap內存儲數據的Entry數組默認是16,如果沒有對Entry擴容機制的話,當存儲的數據一多,Entry內部的鏈表會很長,這就失去了HashMap的存儲意義了。所以HasnMap內部有自己的擴容機制。HashMap內部有:
變量size,它記錄HashMap的底層數組中已用槽的數量;
變量threshold,它是HashMap的閾值,用於判斷是否需要調整HashMap的容量(threshold = 容量*加載因子)
變量DEFAULT_LOAD_FACTOR = 0.75f,默認加載因子爲0.75
HashMap擴容的條件是:當size大於threshold時,對HashMap進行擴容
2. 線程安全性的不同
(1)Hashtable是線程安全的,HashMap是線程非安全的。Hashtable的方法是synchronized的,而HashMap不是。
HashMap是Hashtable的輕量級實現(非線程安全的實現)。
synchronized意味着在一次僅有一個線程能夠更改Hashtable。就是說任何線程要更新Hashtable時要首先獲得同步鎖,其它線程要等到同步鎖被釋放之後才能再次獲得同步鎖更新Hashtable。
(2)HashMap運行效率高:線程非安全,計算hash值的方式更快,擴容直接*2(位運算<<1)
Hashtable運行效率低:線程安全,計算hash值速度慢,擴容方式需要的時間相對長。
(3)HashMap就是爲了提高運行速度設計的,相對於Hashtable而言,其計算hash值更容易產生“碰撞”現象,HashMap產生碰撞就以鏈表結構存儲。HashMap的默認空間較大,時間換空間,都是在提高效率。
(4)在單線程環境時HashMap效率更高,在多線程中Hashtable能避免死鎖,但是JDK1.5新增了concurrent包,引入線程安全的ConcurrentHashMap,使得Map也可以線程安全。它引入了一個“分段鎖”的概念,不是所有方法都是同步的,因此效率相比Hashtable更高。所以儘管HashMap和Hashtable非常相似,但現在Hashtable已經很少用了(何況它還不符合駝峯命名規則)。
3. 對null的存儲支持不同
1 public synchronized V put(K key, V value) {
2 // Make sure the value is not null
3 if (value == null) {
4 throw new NullPointerException();
5 }
6
7 // Makes sure the key is not already in the hashtable.
8 Entry<?,?> tab[] = table;
9 int hash = key.hashCode();
10 int index = (hash & 0x7FFFFFFF) % tab.length;
11 @SuppressWarnings("unchecked")
12 Entry<K,V> entry = (Entry<K,V>)tab[index];
13 for(; entry != null ; entry = entry.next) {
14 if ((entry.hash == hash) && entry.key.equals(key)) {
15 V old = entry.value;
16 entry.value = value;
17 return old;
18 }
19 }
20
21 addEntry(hash, key, value, index);
22 return null;
23 }
4.遍歷方式的內部實現上不同
(1)Hashtable、HashMap都使用了 Iterator。而由於歷史原因,Hashtable還使用了Enumeration的方式 。
(2)HashMap的Iterator是fail-fast迭代器。當有其它線程改變了HashMap的結構(增加,刪除,修改元素),將會拋出ConcurrentModificationException。不過,通過Iterator的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這並不是一個一定發生的行爲,要看JVM。
(3)JDK8之前的版本中,Hashtable是沒有fast-fail機制的。在JDK8及以後的版本中 ,HashTable也是使用fast-fail的
5.計算hash值的方法不同
爲了得到元素的位置,首先需要根據元素的KEY計算出一個hash值,然後再用這個hash值來計算得到最終的位置。
Hashtable直接使用對象的hashCode。hashCode是JDK根據對象的地址或者字符串或者數字算出來的int類型的數值。然後再使用除留餘數發來獲得最終的位置。
Hashtable在計算元素的位置時需要進行一次除法運算,而除法運算是比較耗時的。
HashMap爲了提高計算效率,將哈希表的大小固定爲了2的冪,這樣在取模運算時,不需要做除法,只需要做位運算。位運算比除法的效率要高很多。
HashMap的效率雖然提高了,但是hash衝突卻也增加了。因爲它得出的hash值的低位相同的概率比較高
爲了解決這個問題,HashMap重新根據hashcode計算hash值後,又對hash值做了一些運算來打散數據。使得取得的位置更加分散,從而減少了hash衝突。當然了,爲了高效,HashMap只做了一些簡單的位處理。從而不至於把使用2 的冪次方帶來的效率提升給抵消掉。
6.小結
ps:詳細原理大家可以百度更詳細的底層實現。
更多免費技術資料可關注:annalin1203