Java 8中HashMap沖突解決


在Java 8 之前,HashMap和其他基於map的類都是通過鏈地址法解決沖突,它們使用單向鏈表來存儲相同索引值的元素。在最壞的情況下,這種方式會將HashMap的get方法的性能從O(1)降低到O(n)。為了解決在頻繁沖突時hashmap性能降低的問題,Java 8中使用平衡樹來替代鏈表存儲沖突的元素。這意味着我們可以將最壞情況下的性能從O(n)提高到O(logn)。
在Java 8中使用常量TREEIFY_THRESHOLD來控制是否切換到平衡樹來存儲。目前,這個常量值是8,這意味着當有超過8個元素的索引一樣時,HashMap會使用樹來存儲它們。
這一改變是為了繼續優化常用類。大家可能還記得在Java 7中為了優化常用類對ArrayList和HashMap采用了延遲加載的機制,在有元素加入之前不會分配內存,這會減少空的鏈表和HashMap占用的內存。
這一動態的特性使得HashMap一開始使用鏈表,並在沖突的元素數量超過指定值時用平衡二叉樹替換鏈表。不過這一特性在所有基於hash table的類中並沒有,例如Hashtable和WeakHashMap。
目前,只有ConcurrentHashMap,LinkedHashMap和HashMap會在頻繁沖突的情況下使用平衡樹。

什么時候會產生沖突
HashMap中調用hashCode()方法來計算hashCode。
由於在Java中兩個不同的對象可能有一樣的hashCode,所以不同的鍵可能有一樣hashCode,從而導致沖突的產生。

總結
HashMap在處理沖突時使用鏈表存儲相同索引的元素。
從Java 8開始,HashMap,ConcurrentHashMap和LinkedHashMap在處理頻繁沖突時將使用平衡樹來代替鏈表,當同一hash桶中的元素數量超過特定的值便會由鏈表切換到平衡樹,這會將get()方法的性能從O(n)提高到O(logn)。
當從鏈表切換到平衡樹時,HashMap迭代的順序將會改變。不過這並不會造成什么問題,因為HashMap並沒有對迭代的順序提供任何保證。
從Java 1中就存在的Hashtable類為了保證迭代順序不變,即便在頻繁沖突的情況下也不會使用平衡樹。這一決定是為了不破壞某些較老的需要依賴於Hashtable迭代順序的Java應用。
除了Hashtable之外,WeakHashMap和IdentityHashMap也不會在頻繁沖突的情況下使用平衡樹。
使用HashMap之所以會產生沖突是因為使用了鍵對象的hashCode()方法,而equals()和hashCode()方法不保證不同對象的hashCode是不同的。需要記住的是,相同對象的hashCode一定是相同的,但相同的hashCode不一定是相同的對象。
在HashTable和HashMap中,沖突的產生是由於不同對象的hashCode()方法返回了一樣的值。
以上就是Java中HashMap如何處理沖突。這種方法被稱為鏈地址法,因為使用鏈表存儲同一桶內的元素。通常情況HashMap,HashSet,LinkedHashSet,LinkedHashMap,ConcurrentHashMap,HashTable,IdentityHashMap和WeakHashMap均采用這種方法處理沖突。

從JDK 8開始,HashMap,LinkedHashMap和ConcurrentHashMap為了提升性能,在頻繁沖突的時候使用平衡樹來替代鏈表。因為HashSet內部使用了HashMap,LinkedHashSet內部使用了LinkedHashMap,所以他們的性能也會得到提升。

HashMap的快速高效,使其使用非常廣泛。其原理是,調用hashCode()和equals()方法,並對hashcode進行一定的哈希運算得到相應value的位置信息,將其分到不同的桶里。桶的數量一般會比所承載的實際鍵值對多。當通過key進行查找的時候,往往能夠在常數時間內找到該value。

但是,當某種針對key的hashcode的哈希運算得到的位置信息重復了之后,就發生了哈希碰撞。這會對HashMap的性能產生災難性的影響。

在Java 8 之前, 如果發生碰撞往往是將該value直接鏈接到該位置的其他所有value的末尾,即相互碰撞的所有value形成一個鏈表。

因此,在最壞情況下,HashMap的查找時間復雜度將退化到O(n).

但是在Java 8 中,該碰撞后的處理進行了改進。當一個位置所在的沖突過多時,存儲的value將形成一個排序二叉樹,排序依據為key的hashcode。

則,在最壞情況下,HashMap的查找時間復雜度將從O(1)退化到O(logn)。

 

雖然是一個小小的改進,但意義重大:

1、O(n)到O(logn)的時間開銷。

2、如果惡意程序知道我們用的是Hash算法,則在純鏈表情況下,它能夠發送大量請求導致哈希碰撞,然后不停訪問這些key導致HashMap忙於進行線性查找,最終陷入癱瘓,即形成了拒絕服務攻擊(DoS)。
————————————————
版權聲明:本文為CSDN博主「buder得兒得兒以得兒以得兒得兒」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/cpcpcp123/article/details/52744331


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM