HashMap原理詳解


1、HashMap是存儲鍵值對的數據結構;

2、幾個重要參數:

 

  • 容量,默認為16
  • 負載因子,默認為0.75
  • 擴容極限(暫不十分了解)

說明:當我們不指定任何參數創建HashMap時,就會創建一個容量為16,負載因子為0.75的HashMap,當HashMap中實際的元素個數大於等於16*0.75=12時,會觸發HashMap的resize操作,HashMap的容量會自動擴展一倍。負載因子0.75被證明是性能比較好的取值,通常不會修改,那么只有初始同理會導致頻繁擴容的行為,這是非常耗費資源的操作,所以如果能夠事先估計出容器所需要存儲的容量,就在初始化時修改默認值,即new HashMap(int initialCapacity)。

幾個重要函數:hashCode() 和 equal() 

  • hashCode(),計算key值對應的哈希值,然后確定存儲的數組索引;
  • equal(),確定存儲索引后,在索引處單向鏈表上遍歷比較key值,看是否已經存儲。

說明:這兩個函數對實現HashMap的精確性和正確性至關重要。

3、HashMap的底層實現:Entry數組 + 鏈表 + 紅黑樹。

首先,在HashMap類中有一個Entry的內部類,這個Entry類包含了key-value作為實例變量。沒當向HashMap中存放k-v對時,都會為其實例化一個Entry對象,則個Entry對象就會存儲Entry數組中。而Entry在數組中的具體位置,會根據key的HashCode()方法計算出的哈希值來決定。如果哈希算法設計的足夠好,是不會發生碰撞沖突的,但實際中肯定沒有這么理想,所以在每個索引處,會有一個單向鏈表,來存儲相同索引的Entry對象。

然后,當調用put方法向哈希表中存儲鍵值對時,首先計算key的hashcode,定位到合適的數組索引,然后在該索引上的單向鏈表進行遍歷,用equals函數比較key是否存在。如果存在,則新的value值覆蓋原來的value值;如果不存在,則將新的Entry對象插入到鏈表頭部。且當鏈表長度大於某個值(可能是8)時,鏈表會變為紅黑樹,鏈表的查詢效率為O(n),紅黑樹的查詢效率為O(lgn),可見后者的效率更高。

最后,當需要取出一個Entry對象時,也是根據key的hashCode值來找到其存儲位置,直接取出該Entry。

4、resize在多線程環境下,可能會產生條件競爭。

如果兩個線程都發現HashMap需要重新調整大小,那么它們會同時試着去調整大小。在調整大小時,存儲在鏈表中的元素的次序會反過來,因為在放入新的位置時,HashMap會將Entry對象不斷的插入鏈表的頭部。插入頭部也主要是為了防止尾部遍歷,否則這對key的HashCode相同的Entry每次添加還要定位到尾節點。如果條件競爭發送了,可能會出現環形鏈表,之后當我們get(key)操作時,就有可能發生死循環。形成循環的原因可參考連接http://www.cnblogs.com/andy-zhou/p/5402984.html。

此外,雖然HashTable使用synchronized來保證線程安全,但是它會鎖住整個哈希表,在線程競爭激烈的情況下,效率非常低,所以並不在多線程中經常使用HashTable。

5、多線程中的哈希表ConcurrentHashMap

采用segment分段鎖來保護不同段的數據,這樣在多線程中就既安全又高效。當多線程訪問不同段的數據時,線程間並不存在鎖的競爭,從而可以有效提高並發訪問效率。

6、HashMap和HashTable有什么區別和聯系?

  • 都實現了Map接口;
  • HashMap允許鍵和值時null,而HashTable不允許鍵或者值是null;
  • HashMap不是同步的,多線程不安全;而HashTable是多線程安全的,同步的;
  • HashTable是遺留類,內部實現很多沒優化和冗余。其次在多線環境下,現在也有ConcurrentHashMap代替。

 7、一個解釋很詳細的鏈接:http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532832.html


免責聲明!

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



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