學Java的都知道hashMap的底層是「鏈表散列」的數據結構也也能夠說是hash表。在put的實話先根據key的hashcode從新計算hash值的,而咱們又知道hash是一種算法。因此哈希碼並非徹底惟一的。java
查看哈希碼百科:算法
哈希表能夠說就是數組鏈表,底層仍是數組可是這個數組每一項就是一個鏈表數組
一:爲何說hashmap的put方法是根據key進行hashcode計算的呢?數據結構
查看源碼:app
在查看hash方法,以下:測試
查看putVal方法:this
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,spa
boolean evict) {code
Node [] tab; Node p; int n, i;get
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode )p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
二:hashmap怎麼解決key的hash值衝突問題的?
上圖是源碼截圖,說明:
1:初始化map的大小。默認是16
示例代碼:HashMap map = new HashMap();
2:若是tab爲空,就newNode一個放到鏈表中
示例代碼:map.put("aa",1); 也就是圖-1測試代碼中的1
3:根據key算出的hash值若是存在,且key的值和map中已經存在的值equals了。因此就不處理。
以下圖:
測試代碼如圖-1測試代碼中的1和 2
4:若是p是TreeNode的子類進行putTreeValu
5:若是key的hash值和map中已經存在的key的hash相等且key不一樣的時候,若是數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
如圖-1測試代碼 1和4中key的hesh值都爲3104
圖-1測試代碼
在來看下Node這個內部類:
三:在來看看怎麼get方法
說明:
1:若是鏈表中第一個值的hash值和須要獲取的key的hash值相等的話,就直接取出。
2:若是鏈表first.next !=null就循環查找鏈表中的key,知道查詢到key.equals(k) 取出對應的值。
查看源碼或許感受不懂,那麼畫圖來講明:
總結:
數據結構:哈希表能夠說就是數組鏈表,底層仍是數組可是這個數組每一項就是一個鏈表。
map的put方法:
1:new hashMap的時候初始化默認大小爲16
2:當map.put("aa",1)的時候判斷map沒有值,就把aa算的hash值放到0X004的位置
3:當再次執行map.put("aa",1)的是計算aa的hash值爲3104.此時在OX004的位置已經有數據了。進行判斷存在的key和新put的key是否相同。相同不處理,值覆蓋
4:執行map.put("aa",2)的時候key和已經存在的key相同就直接覆蓋value了
5:執行map.put(3104,"cc")的時候,key的hash值也爲3104.此時數組中OX004已經存在數據,判斷key是否相同。發現3104和aa不相同【注:此時就發生了hash衝突】,那麼就aa這個鏈表前面追加3104
6:執行map.put("bb","cc")。假設bb計算出的hash值是3105就存放在了OX005上。
其餘依次類推
map的get方法:
當執行map.get("bb")的時候先計算出bb的hash值爲3105在對應的位置(也就是0X005)取出第一個判斷值是否是bb若是是就直接取出value.
當執行map.get("aa")的時候先計算出aa的hash值爲3104,去對應的位置取出判斷值(3104)不等於(aa)且還有next。就循環取出進行比較。