1.Hashtable和HashMap
不同點總結如下
① Hashtable是Dictionary的子類,實現了Map接口;HashMap是AbstractMap的子類,是Map接口的一個實現類;
② Hashtable中的方法是同步的,大多數方法如put, get都用用synchronized關鍵字修飾。而HashMap是線程不安全的。在多線程程序中,可以不添加額外操作就可以安全的使用Hashtable,而對HashMap,則需要額外的同步機制,可以使用Collections的一個靜態方法解決:
Map Collections.synchronizedMap(Map m)這個方法返回一個支持同步(線程安全)的Map對象,實質上是用synchronized封裝了HashMap的所有方法。
③在Hashtable中,key和value均不允許為null;而HashMap中,null可以作為key,也可以作為value,null作為key的時候,這樣的鍵只能有一個,但是可以有一個或多個鍵所對應的值為null,當調用HashMap的get方法的時候如果返回null,可以表示HashMap中沒有該鍵,也可以表示該鍵所對應的值為null,因此,在HashMap中不能由get方法來判斷是否存在某個鍵,而應該用containsKey方法來判斷。
④Hashtable中hash數組默認大小是11,增加方式是old*2+1;HashMap中的hash數組默認大小是16,新的容量是原來的2倍;
⑤哈希值的使用不同,Hashtable使用hashSeed與key.hashCode的異或運算的方法:
int hash = hashSeed ^ key.hashCode() int index = (hash & 0x7FFFFFFF) % tab.length;而HashMap並不是直接將對象的hashCode作為哈希值的,而是要把key的hashCode作一些運算以得到最終的哈希值,並且得到的哈希值再和數組長度按位與運算才得到在數組中的位置。
內部實現
在Java中,Hashtable和HashMap的內部結構都是采用Entry數組來實現,一個Entry類包含四個成員變量:hash、key、value、next。
從上圖中我們可以看到,哈希表是有數組+鏈表組成的,一個長度為16的數組中,每個元素存儲的是一個鏈表的頭結點,這些元素通過hash(key)%len,也就是key的哈希值對數組長度取模得到應該放入的數組位置下標。例如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12,所以12、28、108、140都存儲在數組下標為12的位置。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+19y94c/CSGFzaE1hcNDC1PZwdXS6zbvxyKFnZXSy2df3o7o8L3A+CjxwPjxwcmUgY2xhc3M9"brush:java;">//存儲時: int hash = key.hashCode(); int i = hash % Entry[].length; Entry[i] = value; //取值時: int hash = key.hashCode(); int i = hash % Entry[].length; return Entry[i];
注意:
如果數據大小是固定的,那麼最好給HashMap設定一個合理的容量值。HashMap的初始默認容量是16,默認加載因子是0.75,也就是說,如果采用HashMap的默認構造函數,當增加數據時,數據實際容量超過16*0.75=12時,HashMap就擴容,擴容帶來一系列的運算,新建一個是原來容量2倍的數組,對原有元素全部重新哈希,如果你的數據有幾千幾萬個,而用默認的HashMap構造函數,那結果是非常悲劇的,因為HashMap不斷擴容,不斷哈希,
2.Hashtalbe與ConcurrentHashMap區別
相同點:Hashtable和ConcurrentHashMap都是線程安全的,可以在多線程環境中運行;key和value都不能為null
區別:兩者主要是性能上的差異,Hashtable的所有操作都會鎖住整個對象,雖然能夠保證線程安全,但是性能較差;ConcurrentHashMap內部使用Segment數組,其實和Entry類似,它不是加synchronized關鍵字來保證同步,而是基於lock操作(例如put()方法中調用了ReentrantLock類的lock()方法),這樣能避免鎖住整個對象。事實上ConcurrentHashMap可以滿足concurrentLevel個線程並發無阻塞的操作集合對象。
ConcurrentHashMap基於concurrentLevel劃分出了多個Segment來對key-value進行存儲,從而避免每次鎖定整個數組,在默認的情況下,允許16個線程並發無阻塞的操作集合對象,盡可能地減少並發時的阻塞現象。
下面引用一段話來說明ConcurrentHashMap和Hashtable:
Hashtable的任何操作都會把整個表鎖住,是阻塞的。好處是總能獲取最實時的更新,比如說線程A調用putAll寫入大量數據,期間線程B調用get,線程B就會被阻塞,直到線程A完成putAll,因此線程B肯定能獲取到線程A寫入的完整數據。壞處是所有調用都要排隊,效率較低。
ConcurrentHashMap 是設計為非阻塞的。在更新時會局部鎖住某部分數據,但不會把整個表都鎖住。同步讀取操作則是完全非阻塞的。好處是在保證合理的同步前提下,效率很高。壞處 是嚴格來說讀取操作不能保證反映最近的更新。例如線程A調用putAll寫入大量數據,期間線程B調用get,則只能get到目前為止已經順利插入的部分 數據。此外,使用默認構造器創建的ConcurrentHashMap比較占內存,如果程序需要創建巨量ConcurrentHashMap,應該在構造時指定concurrencyLevel
更詳細參考資料:
http://pi88dian88.iteye.com/blog/2008160
http://blog.csdn.net/zhangerqing/article/details/8193118