HashMap實現原理及常見問題

2022-07-12 03:45:11 字數 2563 閱讀 1410

hashmap是基於雜湊表的map介面的實現,用來存放鍵值對(entry),並提供可選的對映操作。使用put(key,value)儲存物件到hashmap中,使用get(key)從hashmap中獲取物件。

hashmap的底層是由陣列加鍊表實現的,是乙個雜湊桶,因為對鍊錶頭部進行增刪操作,所以也稱為棧式鏈結構。鍊錶由 entry物件作為結點,我們把儲存該鍊錶的陣列位置稱之為桶,那麼桶數量就等於陣列的長度。存放資料時時通過key的hashcode來計算hash,得到的hash作為陣列的索引(也就是桶位置)存放物件,當hashcode相同時,則稱之為雜湊衝突,也可稱為「碰撞」。此時通過「拉鍊法」解決衝突。

//entry原始碼

static class nodeimplements map.entry

public final k getkey()

public final v getvalue()

public final string tostring()

補充:在jdk1.8版本之後,在解決雜湊衝突時有了較大的變化,當鍊表長度大於閾值(預設為8)時,將鍊錶轉化為紅黑樹,以減少搜尋時間。

下面由訪問資料的過程進行原理分析:

(1) put(key,value)

在使用put方法傳遞 entry時,會對key呼叫hashcode()方法,接著會對得到的雜湊值再次進行計算,以jdk1.8版本為例,源**如下。

static

final

inthash(object key)

從這裡我們可以看出hash方法對key呼叫hashcode()方法,將得到的雜湊值高16位不變,高16位與低16位進行異或運算。這樣的目的是通過對雜湊值的擾動,盡可能減少碰撞的發生。

此時的雜湊值還不能作為陣列的索引來存放資料,最後還會對擾動後的hash與(陣列長度-1)進行取模運算,即(n - 1) & hash 這裡n為陣列長度,假設n為16 那麼(n-1)的二進位制為1111,將之與hash進行與位運算,1111擷取hash後四位,並保證只是擷取操作,擷取後的hash與擷取前的hash後四位相同,這就保證最後得到的hash能作為索引使資料在陣列長度內盡可能均勻分布,減少碰撞,這種方式和hash%n取餘的結果差不多又不太一樣,通俗點將,取模操作要求n-1的二進位制是111...都是1這種形式,也就必須要求n的值必須為2的次冪,這也解釋了為什麼hashmap規定陣列的長度必須是2的次冪的原因。

重複上述:使用put方法傳遞 entry時,會對key計算hash索引,先判斷陣列table[hash]是否為null,若為null 則入entry,若不為空,就說明發生了碰撞,此時要存入的entry物件的key和桶中的entr

y物件的key

的hash相同,但是它們可能並不相等,所以會呼叫equals方法將要存入的key與桶中的key

一一比對,若均不相等,則存入entry(頭插入法)

,如果相等,會覆蓋原來的entry

.這種解決碰撞的方式就是前面所說的「拉鍊法」。

通過對儲存過程的原理分析,那獲取資料就簡單了,在呼叫get(key)方法時,同樣計算key的hash,通過計算好的hash找到桶位置,然後遍歷鍊錶通過key.equals方法直到找到value值。

(1)關於擴容:

loadfactor: 預設0.75f,代表桶填充程度,loadfactor越趨近於1,那麼陣列中存放的資料(entry)也就越多,也就越密,也就是會讓鍊錶的長度增加,導致查詢元素效率低,loadfactor越趨近於0,陣列的利用率越低,存放的資料會很分散。loadfactor的預設值為0.75f是官方給出的乙個比較好的臨界值。

capacity: 陣列長度,必須為2的次冪,預設為16。hashmap構造中可指定初始長度,會通過乙個演算法轉化成2的次冪

threshold: threshold = capacity * loadfactor,當entry的數量》=threshold的時候,那麼就要考慮對陣列的擴容了,這個的意思就是 衡量陣列是否需要擴增的乙個標準,擴容後,會重新對所有資料進行重新計算,重新存放,這個過程叫做rehash。

//hashmap保證陣列長度為2的次冪的演算法

static

final

int tablesizefor(int

cap)

(2)hashmap與hashtable主要區別為不支援同步和允許null作為key和value,所以如果你想要保證執行緒安全,可以使用concurrenthashmap代替而不是執行緒安全的hashtable,因為hashtable基本已經被淘汰。

(3)如果兩個執行緒都發現hashmap需要調整大小,它們會同時嘗試調整大小,在這個過程中,儲存在鍊錶中的元素次序會反過來,因為移動到新的桶位置的時候,

hashmap並不會將元素放在尾部,而是放在頭部,這是為了避免尾部遍歷,如果條件競爭發生,會發生死迴圈.(注:jdk1.8已經解決了死迴圈的問題。)

(4)key多用string的原因:string是final的,並且重寫了hashmap和equals方法,不可變可以防止鍵的改變,重寫那兩個方法可以減少碰撞的機率.

HashMap底層實現原理及測試

description 本案例用於測試hashmap何時擴容,擴容時是怎麼rehash的,何時轉紅黑樹 條件key key 16 本案例以最少的元素個數 9個 觸發擴容,條件 key key 16 以最少的元素個數 11個 觸發轉紅黑樹 條件key key 128 author wufurun ul...

HashMap實現原理

hashmap 的get 方法 呼叫get方法返回entry public v get object key getentry方法 final entrygetentry object key 對key int hash key null 0 hash key for entrye table in...

HashMap實現原理

資料結構中有陣列和鍊錶來實現對資料的儲存,但這兩者基本上是兩個極端。陣列儲存區間是連續的,占用記憶體嚴重,故空間複雜的很大。但陣列的二分查詢時間複雜度小,為o 1 陣列的特點是 定址容易,插入和刪除困難 鍊錶儲存區間離散,占用記憶體比較寬鬆,故空間複雜度很小,但時間複雜度很大,達o n 鍊錶的特點是...