HashMap的put和get方法原理


JAVA中的數組,在添加或者刪除元素的時候,都會復制一個新數組,比較耗內存。但是數組的遍歷則是非常高效的。鏈表則是相反,遍歷慢(需要遍歷數組,一直找到值相等的元素才算找到),而添加和刪除元素代價低。

有沒有辦法結合兩者的特點,做到尋找元素快,插入元素或者刪除元素代價低呢?答案是利用哈利表。

HashMap put操作

這里寫圖片描述

當使用HashMap的put方法的時候,有兩個問題要解決:

1、長度為16的數組中,元素存儲在哪個位置
2、如果key出現hash沖突,如何解決

第一個問題,HashMap 是使用下面的算法來計算元素的存放位置的。

 int hash = hash(key);
 int i = indexFor(hash, table.length);

    1
    2

首先先hash,之后結合數組的長度進行一個&操作得到得到數組的下標。

第二個問題 則利用Entry類的next變量來實現鏈表,把最新的元素放到鏈表頭,舊的數據則被最新的元素的next變量引用着。
舉個例子,假設元素Entry<"1","1">通過hash算法算出存到下標為0的位置上,后面又添加一個Entry<"2","2">,
假設Entry<"2","2">通過hash算法算出也需要存到下標為0的數組中,那么此時鏈表是下面這個樣子的:

    Entry<”2”,”2”> –> Entry<”1”,”1”>

也即是說,當key出現hash沖突的時候,鏈表中的第一個元素都是后面最新添加進來的那個,之前的則被next變量引用着。雖然這里是插入的動作,但是由於使用了鏈表,所以無需像數組的插入那樣,進行數組拷貝。

HashMap get操作

這個操作的原理就比較簡單,只需要根據key的hashcode算出元素在數組中的下標,之后遍歷Entry對象鏈表,直到找到元素為止。


int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {
    Object k;
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        return e;
}



這里有兩個注意點:
1、這里利用key的hashcode方法和equals方法,所以在使用HashMap的時候,如果使用對象作為key,最好覆寫key的hashcode和equals方法
不然可能出put到HashMap的時候,成功了,但是get的時候卻沒有找到數據
2、如果key hash沖突太多,會造成鏈表過長,在鏈表中查找元素的時候,會比較慢

hash沖突后優化方案

如果出現了大量hash沖突,那么遍歷鏈表的時候,會比較慢。JDK 1.8里面,當鏈表的長度大於閥值(默認為8)的時候,會使用紅黑樹來存儲數據,以便加快key的查詢速度。

總結

HashMap使用了數組+鏈表的方案,做到了讀取快,插入快的目的,但是HashMap還是一些使用上的問題的:
1、線程不安全
2、當容量不夠時,會進行rehash的流程,非常耗資源
————————————————
版權聲明:本文為CSDN博主「Sam_Deep_Thinking」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/linsongbin1/article/details/54667453


免責聲明!

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



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