Hashtable和HashMap

Alexwym發表於2018-07-24

前不久用JAVA寫了一個簡單的雜湊表,但是在和Hashtable、HashMap進行效能測試比較時,發現相差得有點大,於是專門去探究了一下Hashtable和HashMap的一些特性。

一、Hashtable和HashMap的不同

1.繼承物件不同

Hashtable繼承Dictionary類,而HashMap繼承Map類繼承AbstractMap類。

2.雜湊表的初始容量和擴充套件方法不同

Hashtable的預設初始容量是11。而HashMap的預設初始容量是16。並且它的註釋還強調了一句話,“The default initial capacity - MUST be a power of two.”就是這個初始容量的大小必須是2的倍數,這個強調是為了後面用移位操作來取代一些除法操作。因為在計算機中除法時非常耗時間的,而移位操作相對簡單的多。

Hashtable的擴充套件公式為2*n+1,而HashMap的擴充套件公式為2*n。Hashtable的擴充套件方式使得容量大小一直都是奇數,因為只有奇數才有可能是素數,它希望每次擴充套件後的容量都能夠是素數,這樣子就能夠減少雜湊衝突次數。而HashMap則是為了簡化一些計算,把取餘數的除法都變成簡單的位移操作,因此擴充套件後的雜湊表容量都是2的倍數。

3.雜湊碼的計算方法不同——決定新增資料是否可為null

Hashtable的原始碼如下。它的put()方法中明確指出如果value等於null就會丟擲空指標異常的錯誤。但是這裡並沒有說key值不能為null,於是我就嘗試著新增一個key為null,value不為null的值,結果還是丟擲了空指標的錯誤。

既然put函式中沒有指明key必須為null,那就是put()方法裡面呼叫的其他方法有這個限制,初步猜測應該是hashcode()方法,但是這個方法是private型別的,我們在這裡看不到它的具體實現。那麼就只能進行手動測試了。測過結果如下:

果然是hashcode()的問題。null是沒有hashcode的。

我們接著來看一下HashMap。它的put方法中沒有對value進行限制,那麼key呢?既然Object類裡面的Hashcode()不允許為null,如果HashMap直接呼叫這個函式來計算key的雜湊碼,那麼它的key也肯定不能為null。也許正是為了讓key可以為null,他們並沒有直接使用Object自帶的hashcode()方法,而是自己寫了一個hash方法來計算key的雜湊碼,不過其中還是呼叫了hashcode()方法,程式碼如下。如果key==null,那麼它的雜湊碼就為0;如果不為null,再呼叫hashcode()方法,並進行一個異或運算。這樣子HashMap新增的資料key和value都可以為null。

注意:判斷是否含有某個鍵
在HashMap 中,null 可以作為鍵。當get()方法返回null 值時,既可以表示HashMap 中沒有該鍵,也可以表示該鍵所對應的值為null。因此,在HashMap 中不能用get()方法來判斷HashMap 中是否存在某個鍵,而應該用containsKey()方法來判斷。

Hashtable 的鍵值都不能為null,所以可以用get()方法來判斷是否含有某個鍵。返回null的時候說明沒有表中這個鍵值對。

4.遍歷方式不同

Hashtable是使用的是Enumeration(列舉),而HashMap使用iterator(迭代器迭代)。

 

5.是否執行緒安全的

Hashtable是執行緒安全的,它使用了syncronized關鍵字來保證執行緒安全(所謂執行緒安全就是多個執行緒都在使用這個雜湊表時,不會出現資料混亂),而Hashtable的執行緒安全採用的是最簡單粗暴的方法——如果當前已經有一個執行緒在使用這個雜湊表,那麼這個雜湊表就會被鎖住,不允許其他執行緒使用,其他執行緒只能等這個執行緒使用結束後才能使用,這樣一來雖然操作安全,但是也會降低程式的效率。而HashMap不是執行緒安全的。它允許多個執行緒同時操作同一個雜湊表,這樣子效率雖然高,但是雜湊表的結構和資料可能會亂掉。因此多執行緒程式設計優先選擇Hashtable,單執行緒程式設計優先選擇HashMap。

當然還有一種更好的選擇,那就是ConcurrentHashMap,這個繼承自AbstractMap,跟Map很相似。但是它進一步處理HashMap執行緒不安全的問題。它跟Hashtable一樣是執行緒安全的,而相較於Hashtable的整段鎖,它採用的是分段鎖的方法,提高了程式的執行效率。

 

二、兩者的相同點

1.它們的裝載因子都是0.75。

2.都是採用陣列+連結串列的實現

 

三、如何提高雜湊表的效能

1.減低雜湊衝突的次數——Hashtable通過令雜湊表容量儘量為素數來降低

2.避免雜湊表過度失衡——採用紅黑樹來平衡雜湊表

3.雜湊碼的計算速度——HashMap採用移位操作來替代除法

 

小結:分析到這裡我們對Hashtable和HashMap的整體實現和它們之間的區別有了一個大致的理解。當然這裡面還涉及到很多知識,一篇博文肯定是難以講清的,需要我們在JAVA的學習過程中再慢慢去探究。而在這次探究Hashtable和HashMap的過程中,有幾點小感悟在這裡記錄一下。

1.要善於閱讀原始碼——能寫出這些API的都是大佬中的大佬,閱讀原始碼就是一個和大佬交流的過程。

2.emmm,英語要學好——註釋都是英語,有道翻譯啥的都不是那麼靠譜,能夠自己看懂最好

3.看不到的原始碼可以通過樣例測試來探究它的一些內部實現。

相關文章