基礎之HashMap理解

一。JDK1.8以後改動

HashMap使用鏈表法避免哈希衝突(相同的hash值),當鏈表長度大於TREEIFY_THRESHOLD(默認爲8)時,將鏈表轉爲紅黑樹,當小於UNTREEIFY_THRESHOLD(默認爲6)時,又會轉回鏈表以達到性能均衡。

 

二。什麼時候擴容

進行PUT操作時,判斷當前容器元素個數達到閾值(當前數組長度乘加載因子的值 )就自動擴容

擴容是重新計算容量,根據計算出容量定義一個新的容器,將原窗口元素放到新容器裏。

 

三。HashMap爲什麼是線程不安全的

1.put的時候導致的多線程數據不一致。

比如有線程A和B,首先A要插入一個鍵值到HashMap中,首先要計算記錄要插入的hash桶的索引座標,然後取到該桶的鏈表頭結點,此時線程A時間片用完。然後B開始執行,假設A計算出來的hash桶索引和B要插入計算出來的hash桶索引一關的話,那麼B成功插入之後,A再次被調度執行時,它依然有過期的鏈表頭,而且是不知道是過期的。這樣就覆蓋了線程B插入的記錄,B插入的數據就會消失,這樣就數據不一致了

2.resize而引起的死循環

當HashMap自動擴容時,當有2個線程同時檢測到元素個靈長超過閥值(數組大小*負載因子),這時兩個線程都會在put中調用resize();兩個線程同時修改一個鏈表結構會產生一個循環鏈表,接下來再通過 GET()獲取某一個元素就會出現死循環。

 

四。HashMap和HashTable的區別

雙方都實現了Map接口,主要區別有:線程安全,同步,以及速度

1.HashMap是非synchronized的,並可以接受null(可以接受爲null的鏈值),而HashTable則不允許null的鍵值。

2.HashTable是synchronized,意味着HashTable是線程安全的,多個線程可以共享一個HashTable;如果沒有正確的同步,多個線程是不能共享HashMap的。ps:java5有一個ConcurrentHashMap.它可以替代HashTable,且擴展性更好

3.由於HashTable是線程安全的也是synchronized,所以在單線程下它比HashMap要慢,如果不需要同步且單線程那麼使用HashMap性能要更好。

4.HashMap不能保證隨着時間的推移MAP中的元素次序是不變的。

5.迭代器不同,HashMap的迭代器(Iterator)是fail-fast迭代器,而HashTable的enumerator迭代器不是fail-fast的。

 

五。術語介紹

1.sychronized意味着一次只能有一個線程能夠更改HashTable。任何其它線程要更新時要先獲取同步鎖,其它線程要等 同步鎖被釋放才能再次獲取同步鎖更新HashTable.

 

六。HashMap可以通過下面語句進行同步

Map m = Collections.synchronizeMap(hashMap);

 

七。JDK1.7和JDK1.8裏面HashMap的不同點

1.1.7中用的是頭插法,1.8及之後用的是尾插法,原因:1.7是單鏈表進行的縱向延伸,頭插法能提高插入效率,但是也會容易出現逆序環形鏈表死循環問題,1.8之後加入了紅黑樹使用尾插法,避免出現逆序環形鏈表死循環問題

2.擴容後數據存儲位置的計算方式不一樣

1)在1.7中直接用hash值和需要擴容的二進制數進行&(這就是爲什麼擴容的時候爲啥一定是2的多少次冪的原因,因爲2的N次冪的情況下最後一位二進制數才一定是1,能最大程序減少碰撞)

2)在1.8中,直接用的擴容 前的原始位置——擴容的大小值,而不是1.7的那種異域的方法,這種方式就相當只要判斷hash值的新增與運算的位是0還是1,就直接迅速算出擴容 後的存儲方式。

3.在1.7中使用的是數組+單鏈表的數據結構,但是在1.8及以後,用的是數組+鏈表+紅黑樹的數據結構(當鏈表的深度達到默認閥值(8)的時候,就會自動擴容把鏈表轉成紅黑樹的數據結構把時間複雜度從0(N)變成0=O(logN)提高效率)

 

八。1.8及以後爲什麼不用二叉樹而用紅黑樹

選擇紅黑樹是爲了解決二叉查找樹的缺陷,二叉樹在特殊情況下會變成一條線性結構,遍歷查找會非常慢,而紅黑樹是平衡二叉樹,插入數據後通過左旋右旋變色來保持平衡。引用紅黑樹是爲了查找數據快,但也會有資源損耗,所以要長度大於8纔會使用紅黑樹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章