日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
重溫數(shù)據(jù)結(jié)構(gòu)經(jīng)典:HashCode及HashMap原理

一、HashCode為什么使用31作為乘數(shù)

1、選擇數(shù)字31是因為它是一個奇質(zhì)數(shù),如果選擇一個偶數(shù)會在乘法運算中產(chǎn)生溢出,導致數(shù)值信息丟失,因為乘二相當于移位運算。選擇質(zhì)數(shù)的優(yōu)勢并不是特別的明顯,但這是一個傳統(tǒng)。

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都做網(wǎng)站、網(wǎng)站設(shè)計、銅陵網(wǎng)絡(luò)推廣、小程序制作、銅陵網(wǎng)絡(luò)營銷、銅陵企業(yè)策劃、銅陵品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)公司為所有大學生創(chuàng)業(yè)者提供銅陵建站搭建服務(wù),24小時服務(wù)熱線:028-86922220,官方網(wǎng)址:www.cdcxhl.com

2、數(shù)字31有一個很好的特性,即乘法運算可以被移位和減法運算取代,來獲取更好的性能:31 * i == (i << 5) - i,現(xiàn)代的 Java 虛擬機可以自動地完成這個優(yōu)化。

二、數(shù)組與鏈表的特點

數(shù)組的特點是:尋址容易,插入和刪除困難。

鏈表的特點是:尋址困難,插入和刪除容易。

三、HashMap原理

  • 允許null健及null值,null健,值為0。
  • HashMap 不保證鍵值對的順序,操作時,鍵值對的順序會發(fā)生變化。
  • HashMap是非線程安全類,在多線程環(huán)境下會發(fā)生問題。
  • HashMap是JDK 1.2時推出的,底層基于散列算法實現(xiàn)。
  • 在JDK 1.8中涉及比較多:1、散列表實現(xiàn)、2、擾動函數(shù)、3、初始化容量、4、負載因子、5、擴容元素拆分、6、鏈表樹化、7、紅黑樹、8、插入、9、查找、10、刪除、11、遍歷、12、分段鎖等。

(1) 擾動函數(shù)

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

擾動函數(shù)就是為了增加隨機性,讓數(shù)據(jù)元素更加均衡地散列,減少碰撞。

(2) 負載因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

負載因子,可以理解成一輛車可承重重量超過某個閥值時,把貨放到新的車上。在 HashMap 中,負載因子決定了數(shù)據(jù)量多少了以后進行擴容。

0.75 是一個默認構(gòu)造值,在創(chuàng)建 HashMap 也可以調(diào)整,如何希望用更多的空間換取時間,可以把負載因子調(diào)得更小一些,減少碰撞。

(3) 擴容元素拆分

擴容最直接的問題,就是需要把元素拆分到新的數(shù)組中,在JDK1.7中,會重新計算哈希值,但在JDK1.8后,進行了優(yōu)化。

  1. 擴容時計算出新的newCap、newThr,這是兩個單詞的縮寫,一個是Capacity ,另一個是閥Threshold。
  2. newCap用于創(chuàng)新的數(shù)組桶 new Node[newCap]。
  3. 隨著擴容后,原來那些因為哈希碰撞,存放成鏈表和紅黑樹的元素,都需要進行拆分存放到新的位置中。

(4) 數(shù)據(jù)遷移

(5) 數(shù)據(jù)插入

  1. 首先進行哈希值的擾動,獲取一個新的哈希值。(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)。
  2. 判斷tab是否為空或者長度為0,如果是則進行擴容操作。
  3. if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length。
  4. 根據(jù)哈希值計算下標,如果對應(yīng)小標正好沒有存放數(shù)據(jù),則直接插入即可否則需要覆蓋。tab[i = (n - 1) & hash])。
  5. 判斷tab[i]是否為樹節(jié)點,否則向鏈表中插入數(shù)據(jù),否則向樹中插入節(jié)點。
  6. 如果鏈表中插入節(jié)點的時候,鏈表長度大于等于8,則需要把鏈表轉(zhuǎn)換為紅黑樹。treeifyBin(tab, hash)。
  7. 最后所有元素處理完成后,判斷是否超過閾值;threshold,超過則擴容。
  8. treeifyBin,是一個鏈表轉(zhuǎn)樹的方法,但不是所有的鏈表長度為8后都會轉(zhuǎn)成樹,還需要判斷存放key值的數(shù)組桶長度是否小于64 MIN_TREEIFY_CAPACITY。如果小于則需要擴容,擴容后鏈表上的數(shù)據(jù)會被拆分散列的相應(yīng)的桶節(jié)點上,也就把鏈表長度縮短了。

(6) 鏈表樹化

  1. 鏈表樹化的條件有兩點;鏈表長度大于等于8、桶容量大于64,否則只是擴容,不會樹化。
  2. 鏈表樹化的過程中是先由鏈表轉(zhuǎn)換為樹節(jié)點,此時的樹可能不是一顆平衡樹。同時在樹轉(zhuǎn)換過程中會記錄鏈表的順序,tl.next = p,這主要方便后續(xù)樹轉(zhuǎn)鏈表和拆分更方便。
  3. 鏈表轉(zhuǎn)換成樹完成后,再進行紅黑樹的轉(zhuǎn)換。先簡單介紹下,紅黑樹的轉(zhuǎn)換需要染色和旋轉(zhuǎn),以及比對大小。在比較元素的大小中,有一個比較有意思的方法,tieBreakOrder加時賽,這主要是因為HashMap沒有像TreeMap那樣本身就有Comparator的實現(xiàn)。

(7) 紅黑樹轉(zhuǎn)鏈

在轉(zhuǎn)換樹的過程中,記錄了原有鏈表的順序,紅黑樹轉(zhuǎn)鏈表時候,直接把TreeNode轉(zhuǎn)換為Node,因為記錄了鏈表關(guān)系,所以替換過程很容易。

(8) 查找

  1. 擾動函數(shù)的使用,獲取新的哈希值。
  2. 下標的計算, tab[(n - 1) & hash])。
  3. 確定了桶數(shù)組下標位置,接下來就是對紅黑樹和鏈表進行查找和遍歷操作了。

(9) 刪除

  1. 刪除的操作也比較簡單,沒有太多的復雜的邏輯。
  2. 另外紅黑樹的操作因為被包裝了,只看使用上也是很容易。

(10) 遍歷

  1. 這里我們要設(shè)定一個既有紅黑樹又有鏈表結(jié)構(gòu)的數(shù)據(jù)場景
  2. 為了可以有這樣的數(shù)據(jù)結(jié)構(gòu),我們最好把 HashMap 初始長度設(shè)定為 64,避免在

鏈表超過 8 位后擴容,而是直接讓其轉(zhuǎn)換為紅黑樹。

  1. 找到 18 個元素,分別放在不同節(jié)點(這些數(shù)據(jù)通過程序計算得來)。
  2. 桶數(shù)組 02 節(jié)點:24、46、68。
  3. 桶數(shù)組 07 節(jié)點:29。
  4. 桶數(shù)組 12 節(jié)點:150、172、194、271、293、370、392、491、590。

測試代碼

public void test_Iterator() {
Map map = new HashMap(64);
map.put("24", "Idx:2");
map.put("46", "Idx:2");
map.put("68", "Idx:2");
map.put("29", "Idx:7");
map.put("150", "Idx:12");
map.put("172", "Idx:12");
map.put("194", "Idx:12");
map.put("271", "Idx:12");
System.out.println("排序01:");
for (String key : map.keySet()) {
System.out.print(key + " ");
}
map.put("293", "Idx:12");
map.put("370", "Idx:12");
map.put("392", "Idx:12");
map.put("491", "Idx:12");
map.put("590", "Idx:12");
System.out.println("\n\n排序02:");
for (String key : map.keySet()) {
System.out.print(key + " ");
}
map.remove("293");
map.remove("370");
map.remove("392");
map.remove("491");
map.remove("590");
System.out.println("\n\n排序03:");
for (String key : map.keySet()) {
System.out.print(key + " ");
}
}
  1. 添加元素,在 HashMap 還是只鏈表結(jié)構(gòu)時,輸出測試結(jié)果 01。
  2. 添加元素,在 HashMap 轉(zhuǎn)換為紅黑樹的時候,輸出測試結(jié)果 02。
  3. 刪除元素,在 HashMap 轉(zhuǎn)換為鏈表結(jié)構(gòu)時,輸出測試結(jié)果 03。

結(jié)果如下:

排序01:
24 46 68 29 150 172 194 271
排序02:
24 46 68 29 271 150 172 194 293 370 392 491 590
排序03:
24 46 68 29 172 271 150 194
Process finished with exit code 0

這一篇 API 源碼以及邏輯與上一篇數(shù)據(jù)結(jié)構(gòu)中擾動函數(shù)、負載因子、散列表實現(xiàn)等,內(nèi)容的結(jié)合,算是把 HashMap 基本常用技術(shù)點,梳理完成了。但知識絕不止于此,這里還有紅黑樹的相關(guān)技術(shù)內(nèi)容,后續(xù)會進行詳細。

除了 HashMap 以外還有 TreeMap、ConcurrentHashMap 等,每一個核心類都有一與之相關(guān)的核心知識點,每一個都非常值得深入研究。這個燒腦的過程,是學習獲得的知識的最佳方式。


名稱欄目:重溫數(shù)據(jù)結(jié)構(gòu)經(jīng)典:HashCode及HashMap原理
網(wǎng)站鏈接:http://www.dlmjj.cn/article/dhiocjp.html