JAVA 資料結構 HashMap
JAVA-HashMap
HashMap是一種特殊的資料結構。既然是資料結構,就一定有應用的場景,不然哪些JDK大牛搞這個幹啥。
問題:
Q1 它能幹啥?
Q2 有什麼特別的?
Q3 有什麼優點?
A1: 當我們需要存放key-value pair 鍵值對的時候,就可以使用這個結構
A2: 名字裡面能帶hash,說明它和hash有關;而且可以允許存放‘空’的鍵值對。
A3: 既然是map,說明有快速查詢value的優勢。
HashMap它很好的解決了儲存和查詢K-V的問題。
在JDK1.7中,它自身的結構是陣列+連結串列來實現的,陣列中的每一個元素都是一個連結串列。通過計算key的hashcode來把hashcode相同的存放在相同的陣列元素中。
在JDK1.8中,底層結構發生了變化當一個Entry鏈中大於8個元素時,就會轉變為紅黑樹
結構已經有了初步的認識,怎麼用,用的時候關注哪些問題?
容量問題:因為HashCode計算返回int
範圍高達+-21億,不算負數,所以陣列範圍就可以支援那麼大。但是超大的容量會帶來各種各樣的問題,所以HashMap的預設容量是16。
使用中會不會超出容量?
當然有可能超過預設的16,當想hashmap新增超過一定數量的KV,就會觸發自動擴容,每次容量增加一倍,即16-32-64……。如果hashmap經常觸發擴容的話,會導致效能影響很大,所以儘量設定好容量之後,避免擴容。
HashMap什麼時候會擴容,擴容的原則是什麼?擴容的規則。
HashMap有一個引數負載因子loadfactor,JDK中預設0.75,如果hashmap中的kv到達容量*
*
0.75=12,到達12個KV就會自動擴容從16擴充套件到32,而且要重新確定索引位置。當然使用者也可以自己定義負載因子,但是不建議自己定義。因子太大,空間利用率越大,但是查詢速度就會變慢,因子太小,空間就會有很多浪費。
原始碼
類定義
類定義常量
鍵值對
擴容
put
get
連結串列轉紅黑樹
put
首先檢查table是不是null,如果為空,就resize初始化table。
如果計算(n - 1) & hash
值對應的桶
為空就直接插入一個新node
。如果已經存在了就要判斷hash,k,v
,如果hash,key
都一樣,就會根據條件將之前的v
覆蓋;如果hash,key
node
(而且會根據節點型別,插入樹節點還是連結串列節點),如果k,v
數量大於閾值,就會觸發擴容resize
get
根據key 計算hash
,並且去查詢HashMap是否存在這個節點。根據hash和key確認(n - 1) & hash
和key是否相等,如果有就取出返回,如果沒有就沒有查到。
一些問題
看完以上,對HashMap基本算是瞭解了。還有一些比較雜的問題,可能大家都會有一些疑惑,但是並沒有在意,因為不會影響使用。
hashMap的長度是2的冪次?
通過(n - 1) & hash
來決定桶的位置?
簡單來說,是為了更有效的減少碰撞的機率,從而能夠有效提高效率。如果想要詳細瞭解,可以去看一下hashmap的hash原始碼。
HashMap為什麼執行緒不安全
執行緒安全問題必然是在多執行緒的情況下發生,如果有2個執行緒threadA
,threadB
,
threadA
要插入一個kv到hashmap中,在做put的操作時, 時間片剛好結束,threadB
開始執行,要把一個kv插到hashmap中,併成功插入。如果2個執行緒使用的桶
是一樣的,這個時候threadA
就會把threadB
的記錄覆蓋掉。