下面直接來乾貨,先說這三個Map的區別:php
HashMap的初始值還要考慮加載因子:html
HashMap和Hashtable都是用hash算法來決定其元素的存儲,所以HashMap和Hashtable的hash表包含以下屬性:面試
除此以外,hash表裏還有一個「負載極限」,「負載極限」是一個0~1的數值,「負載極限」決定了hash表的最大填滿程度。當hash表中的負載因子達到指定的「負載極限」時,hash表會自動成倍地增長容量(桶的數量),並將原有的對象從新分配,放入新的桶內,這稱爲rehashing。算法
HashMap和Hashtable的構造器容許指定一個負載極限,HashMap和Hashtable默認的「負載極限」爲0.75,這代表當該hash表的3/4已經被填滿時,hash表會發生rehashing。數組
「負載極限」的默認值(0.75)是時間和空間成本上的一種折中:安全
程序猿能夠根據實際狀況來調整「負載極限」值。多線程
Hashtable和HashMap都實現了Map接口,可是Hashtable的實現是基於Dictionary抽象類的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。併發
HashMap基於哈希思想,實現對數據的讀寫。當咱們將鍵值對傳遞給put()方法時,它調用鍵對象的hashCode()方法來計算hashcode,而後找到bucket位置來存儲值對象。當獲取對象時,經過鍵對象的equals()方法找到正確的鍵值對,而後返回值對象。HashMap使用鏈表來解決碰撞問題,當發生碰撞時,對象將會儲存在鏈表的下一個節點中。HashMap在每一個鏈表節點中儲存鍵值對對象。當兩個不一樣的鍵對象的hashcode相同時,它們會儲存在同一個bucket位置的鏈表中,可經過鍵對象的equals()方法來找到鍵值對。若是鏈表大小超過閾值(TREEIFY_THRESHOLD,8),鏈表就會被改造爲樹形結構。性能
在HashMap中,null能夠做爲鍵,這樣的鍵只有一個,但能夠有一個或多個鍵所對應的值爲null。當get()方法返回null值時,便可以表示HashMap中沒有該key,也能夠表示該key所對應的value爲null。所以,在HashMap中不能由get()方法來判斷HashMap中是否存在某個key,應該用containsKey()方法來判斷。而在Hashtable中,不管是key仍是value都不能爲null。優化
Hashtable是線程安全的,它的方法是同步的,能夠直接用在多線程環境中。而HashMap則不是線程安全的,在多線程環境中,須要手動實現同步機制。
Hashtable與HashMap另外一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。因此當有其它線程改變了HashMap的結構(增長或者移除元素),將會拋出ConcurrentModificationException,但迭代器自己的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這並非一個必定發生的行爲,要看JVM。
先看一下簡單的類圖:
從類圖中能夠看出來在存儲結構中ConcurrentHashMap比HashMap多出了一個類Segment,而Segment是一個可重入鎖。
ConcurrentHashMap是使用了鎖分段技術來保證線程安全的。
鎖分段技術:首先將數據分紅一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據的時候,其餘段的數據也能被其餘線程訪問。
ConcurrentHashMap提供了與Hashtable和SynchronizedMap不一樣的鎖機制。Hashtable中採用的鎖機制是一次鎖住整個hash表,從而在同一時刻只能由一個線程對其進行操做;而ConcurrentHashMap中則是一次鎖住一個桶。
ConcurrentHashMap默認將hash表分爲16個桶,諸如get、put、remove等經常使用操做只鎖住當前須要用到的桶。這樣,原來只能一個線程進入,如今卻能同時有16個寫線程執行,併發性能的提高是顯而易見的。