HashMap與Hashtable區別及HashMap實現原理
阿新 • • 發佈:2019-02-03
Map是編碼過程中經常使用到的容器,而HashMap和Hashtable都實現了Map的,所以我們往往會把兩者進行對比。
HashMap和Hashtable區別
- Hashtable是執行緒安全的,HashMap是非執行緒安全的。Hashtable是基於老的Diactionary類實現的,HashMap是Java 1.2引進Map介面後的重新實現。Hashtable的方法,進行了鎖同步,可以支行於多執行緒環境。HashMap需要程式設計人員自在己為其提供同步,才能執行多執行緒中。常用的方法是:利用Collections類的靜態的synchronizedMap()方法,它建立一個執行緒安全的Map物件或者是使用ConcurrentHashMap。
- 由於HashMap非是執行緒安全的,所以效能要明顯優於Hashtable。
如果多執行緒情況下呢?Hashtable的實現與Collections的靜態方法synchronizedMap實現有點類似,使用synchronized來保證執行緒安全。所以如果線上程競爭激烈的情況下,效率就會非常低下,而且執行緒的安全性還緊限於get、put之類的簡單操作。這時ConcurrentHashMap可能是最好的選擇。 - 對空值處理:HashMap中允許有一個空的key,和任意個空的value,對Hashtable put空的key、value則會報錯。如果判斷HashMap是否存在某個key值,最準確的是使用containKey,而不是使用對get的值判空,因為你put的也許是個空值。
transient Entry[] table;//儲存元素的實體陣列 transient int size;//存放元素的個數 int threshold; //臨界值 final float loadFactor; //載入因子 transient int modCount;//被修改的次數
當實際大小超過臨界值時,會進行擴容threshold = 載入因子*容量。載入因子越大,填滿的元素越多,好處是,空間利用率高了,衝突的機會加大了.連結串列長度會越來越長,查詢效率降低。反之,載入因子越小,填滿的元素越少,好處是:衝突的機會減小了,但:空間浪費多了.表中的資料將過於稀疏(很多空間還沒用,就開始擴容了)。衝突的機會越大,則查詢的成本越高.因此,必須在 "衝突的機會"與"空間利用率"之間尋找一種平衡與折衷. 這種平衡與折衷本質上是資料結構中有名的"時-空"矛盾的平衡與折衷。如果機器記憶體足夠,並且想要提高查詢速度的話可以將載入因子設定小一點;相反如果機器記憶體緊張,並且對查詢速度沒有什麼要求的話可以將載入因子設定大一點。不過一般我們都不用去設定它,讓它取預設值0.75就好了。
每個Entry物件都記錄Entry連結串列的下一個結點。當查詢的key值出現hash碰撞,我們就需要遍歷連結串列了。
我們再來看看,hash碼是如何對映陣列下標的:
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
雜湊表的容量一定要是2的整數次冪,這樣HashMap中則通過h&(length-1)的方法來代替取模,就實現了均勻的雜湊。同時在陣列進行擴容時,擴容前的元素的下標位置是不變的,避免了陣列元素的重排。但是,我們仍然要避免陣列的擴容,因為它依然很大的降低的了map的put效能,這也是HashMap定義載入因子的原因所在。
那我們該如果初始化載入因子的值呢?這和Map值個數有關係,和Map的每個key值有關係。為了達到這種平衡,我們只能通過真實資料,場景進行,測試來獲得這個最佳的載入因子。如果不確定就用預設值好了,沒有更好的方法。