hashMap對各位小夥們來說,沒有不知道的了,使用過的人想必或多或少的都了解一點hashMap的底層實作原理,總結來說就是,數組+連結清單,至于源碼的實作,大家可參看源碼,今天想說的是hashMap是怎麼解決hash沖突的呢?
首先看一張圖,

從這張圖也大概可以看出來,hashMap維護的是一個數組,數組裡面的每個單元又是一個個連結清單,那麼為什麼會産生hash沖突呢?這也就是接下來要探讨的問題。
既是數組,必然會有長度,當我們在往數組中插入資料的時候,不管是什麼類型的資料,對于數組來說,就是占據了某個下标對應的空間,那麼當加入的資料越來越多的時候,是否會出現多個資料占據同一個位置呢?答案是肯定的,這就是hash沖突産生的原始因素;
首先,我們先弄清楚幾個概念,對于hashMap或者其他類似的map來說,我們往裡面添加資料的時候,并不是直接往數組裡面加,而是通過計算這個插入資料的hash值,即通過一個hash的算法,然後把這個值加進去,以後再去查找資料的時候,hashMap同樣會根據你的key,倒推出這個hash值然後取出資料,即這個hash值可以了解為插入值對應的數組下表;
但通過實驗我們可以發現,hash函數計算不同的key的時候,可能得到相同的hash值,這樣一來,如果再用這個hash值作為數組的辨別這個值的下标,就無法定位這個值了,這個時候沖突就發生了;
下面我們用代碼來模拟一下這個使用開發位址法解決hash沖突的問題,首先定義一個對象,這裡為Info,為了更接近真實場景,我們這裡的屬性都為字元串,
什麼是開放位址法呢?
**
接下來手工寫一個hashTable,用于模拟hashMap,
可以看到,我們是通過對要插入的數值先進行hash編碼,再對數值的長度進行取模i,這樣得到的位置總能夠落在數值的長度内,
裡面有個地方可能不太好了解,就是在插入資料的時候,我們使用while循環進行插入,既然是開發位址,也就是說數組的每一個閑置的空間我們都能使用,前提是這個位置沒有被其他的值占用,由于數組是連續的,是以我們需要循環的去尋找一個這樣的位置,是以才有 ++hashVal這段代碼,直到找到了一個空位,然後我們把資料插入進去,
運作測試main方法,我們看到,資料成功插入,但通過hash函數計算得到的“a”和"ct"卻是一樣的,再一次印證了我們前面所說的問題,
以上便是所說的采用開發位址法解決hash沖突的解決方法,但這樣就萬無一失了嗎?
我們考慮一下,資料的長度是有限的,但我們可能會往數組裡面添加很多資料進去,數組總有被填滿的時候,那樣開發位址法也不管用了,當然,實際業務中,如果可以預料資料的大小,我們可以采用這樣的方式解決部分問題,但問題是這樣确實不是萬無一失的解決辦法,
更合适的方式是什麼呢?其實就是hashMap中使用較多的鍊位址法,也就是一開始我們圖中展示的,基本結構仍然是一個數組,但是數組的每個單元維護的不再是一個個資料,而是一個個連結清單,也就是類似于linkedList這樣的結構,當新插入的多個資料通過計算hash函數得到的是相同的數組下标時候,我們隻需要把值往這個索引位置維護的連結清單中插入即可,什麼是鍊位址法呢?
下面用代碼來實作一下這個過程,同上面所有不同的是,連結清單中的結構我們通過是維護者一個個節點,即Node ,對連結清單結構不熟悉的同學可以先自行百度一下,不是很難,
1、定義一個對象Info,
2、定義一個Node作為連結清單中的基本存儲單元,
3、定義一個連結清單,
4、模拟hashMap的幾個方法,
和上面開發位址法插入資料和查找資料不同,此種方式進行資料查找的時候,其實是進行兩次查到的,第一次定位數組中的位置,第二次去到連結清單中,調用連結清單的查找方法進行查找,這一點值得注意,插入和删除的思想也是類似,
下面我們來測試一下,可以看到,依然達到了效果,說明我們模拟的鍊位址法也生效了,
以上就是通過開發位址法和鍊位址法解決hash沖突的兩種方式,希望對大家了解hashMap的底層原理有所幫助…感謝觀看!