HashMap學習筆記
初始容量
在構造HashMap的時候根據預期的entry數量考慮初始容量和負載因子侠鳄,這樣可以盡可能的避免rehash。如果有很多kv要存在HashMap中死宣,創(chuàng)建一個足夠大的實例來存儲要比讓hashmap需要擴容時自動rehash要更加高效伟恶。負載因子
load factor = entry.size / hashmap capacityComparable
使用許多相同hashCode的key肯定會降低性能。為了改善沖突毅该,當key是Comparable時博秫,這個類會通過在key之間比較順序來打破紐帶。從java8開始眶掌,當hash 沖突的長度超過一定的閾值(8)并且map的容量超過一定的閾值(64)挡育,hashMap會把鏈表變成一個紅黑樹(一直平衡)。當HashMap實現嘗試在樹中查找新條目的位置時朴爬,首先檢查當前值和新值是否是Comparable即寒。如果不是的話,只能通過tieBreakOrder(Object a, Object b)
來比較召噩,這個方法會先比較class name蒿叠,然后使用System.identityHashCode
。如果是Comparable
的蚣常,那么事情就簡單了,通過compareTo
接口就可以比較了痊银。值得一提的是抵蚊,根據compareTo方法(該方法返回0),當兩個Comparable鍵結果相等時溯革,將使用相同的tieBreakOrder方法贞绳。-
resize(初始化table或者對table進行double 擴容)
- 如果沒有初始化,那么此方法就是初始化table的方法致稀,默認的capacity是16冈闭,負載因子是0.75。
- 已經初始化抖单,那么要對
-
rehash過程(參考鏈接)
- java7 resize過程中每一個元素都要重新通過indexFor(hash)計算位置萎攒,然后將元素插入到新數據到鏈表頭部,形成了反序(在并發(fā)的情況下有可能形成回環(huán))矛绘。
- java8 采用2倍擴容耍休,擴容后,resize過程中货矮,每一個元素通過與oldCap取&羊精,看看結果是1還是0,如果是0的話囚玫,分配新表的原索引位置喧锦,如果是1的話读规,分配到新表的(原索引位置+oldCap)位置,兩者都是將元素插入到鏈表尾(保持了元素相對順序沒有發(fā)生變化)燃少。這個原因是在根據hash定位數組索引位置的時候是通過hash&(n-1)來做的束亏,2倍擴容以后hash&(n-1)的結果只有高位會發(fā)生變化,只有發(fā)生變化的才需要遷移到新的索引位置供汛,可以通過hash&oldCap得到高位的值枪汪,通過高位的值是否為1來判斷采取的動作
線程安全
HashMap是非線程安全的,如果有多個線程并發(fā)地對hashmap進行結構性變化怔昨,那么就必須額外的同步雀久。通常是由同步持有HashMap實例的對象來完成對hashmap的同步的。如果沒有這樣的對象存在趁舀,這個map應該被Collections.synchronizedMap
方法來包裝赖捌,最好是在創(chuàng)建時完成,來阻止意外的非同步的對hashmap的訪問矮烹。HashMap的所有視圖方法都是Fail-Fast的越庇。