HashMap主要是用數組來存儲數據的,咱們都知道它會對key進行哈希運算,哈系運算會有重複的哈希值,對於哈希值的衝突,HashMap採用鏈表來解決的。
HashMap是線程不安全的,若是被多個線程共享的操做,將會引起不可預知的問題,據sun的說法,在擴容時,會引發鏈表的閉環,在get元素時,就會無限循環,後果是cpu100%。
java
Open addressing和Chaining(也稱外部拉鍊法)是兩種不一樣的解決hash衝突的策略。當多個不一樣的key被映射到相同的slot時,chaining方式採用鏈表保存全部的value。而Open addressing則嘗試在該slot的鄰近位置查找,直到找到對應的value或者空閒的slot, 這個過程被稱做probing。常見的probing策略有Linear probing(線性探測),Quadratic probing(二次探測)和Double hashing(雙重hash)。
算法
Chaining 數組
在分析open addressing策略以前,首先簡單介紹一下大多數的Java 核心集合類採用的chaining策略,以便比較。 java.util.HashMap有一個Entry[]類型的成員變量table,其每一個非null元素都是一個單向鏈表的表頭。 緩存
Chaining策略的主要缺點是須要經過Entry保存key,value以及指向鏈表下個節點的引用(Map.Entry就有四個成員變量),這意味着更多的內存使用(尤爲是當key,value自己使用的內存很小時,額外使用的內存所佔的比例就顯得比較大)。此外鏈表對CPU的高速緩存不太友好。
安全
Open Addressing 性能
Linear probing (線性探測、線性再哈希) 優化
兩次查找位置的間隔爲一固定值,即每次查找時在原slot位置的基礎上增長一個固定值(一般爲1),例如:P = (P + 1) mod SLOT_LENGTH。其最大的優勢在於計算速度快,另外對CPU高速緩存更友好。其缺點也很是明顯: spa
假設key1,key2,key3的hash code都相同而且key1被映射到slot(p),那麼在計算key2的映射位置時須要查找slot(p), slot(p+1),計算key3的映射位置時須要查找slot(p), slot(p+1),slot(p+2)。也就是說對於致使hash衝突的全部key,在probing過程當中會重複查找之前已經查找過的位置,這種現象被稱爲clustering(彙集)。 .net
Quadratic probing (二次探測、非線性再哈希) 線程
兩次查找位置的間隔線性增加,例如P(i) = (P + c1*i + c2*i*i) mod SLOT_LENGTH,其中c1和c2爲常量且c2不爲0(若是爲0,那麼降級爲Linear probing)。 Quadratic probing的各方面性能介於Linear probing和Double hashing之間。
Double hashing (雙重哈希)
兩次查找位置的間隔爲一固定值,可是該值經過另一個hash算法生成,例如P = (P + INCREMENT(key)) mod SLOT_LENGTH,其中INCREMENT即另一個hash算法。如下是個簡單的例子:
H(key) = key mod 10
INCREMENT(key) = 1 + (key mod 7)
P(15): H(15) = 5;
P(35): H(35) = 5, 與P(15)衝突,所以須要進行probe,位置是 (5 + INCREMENT(35)) mod 10 = 6
P(25): H(25) = 5, 與P(15)衝突,所以須要進行probe,位置是 (5 + INCREMENT(25)) mod 10 = 0
P(75): H(75) = 5, 與P(15)衝突,所以須要進行probe,位置是 (5 + INCREMENT(75)) mod 10 = 1
從以上例子能夠看出,跟Linear probing相比,減小了重複查找的次數。
Load Factor
基於open addressing的哈希表的性能對其load factor屬性值很是敏感。若是該值超過0.7 (Trove maps/sets的默認load factor是0.5),那麼性能會降低的很是明顯。因爲hash衝突致使的probing次數跟(loadFactor) / (1 - loadFactor)成正比。當loadFactor爲1時,若是哈希表中的空閒slot很是少,那麼可能會致使probing的次數很是大。
Open addressing in gnu.trove.THashMap
GNU Trove (http://trove4j.sourceforge.net/) 是一個Java 集合類庫。在某些場景下,Trove集合類庫提供了更好的性能,並且內存使用更少。如下是Trove中跟open addressing相關的幾個特性:
跟java.util.HashMap相比,gnu.trove.THashMap沒有Entry[] table之類的成員變量,而是分別經過Object[] _set,V[] _values直接保存key和value。在邏輯上,Object[] _set中的每一個元素都有三種狀態: