最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入探究HashMap二次Hash原因

 更新時(shí)間:2023年01月04日 11:25:26   作者:程序員小潘  
在java開(kāi)發(fā)中,HashMap是最常用、最常見(jiàn)的集合容器類(lèi)之一,文中通過(guò)示例代碼介紹HashMap為啥要二次Hash,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

1. 前言

HashMap對(duì)于Java程序員來(lái)說(shuō)一定不陌生,除了平時(shí)開(kāi)發(fā)會(huì)經(jīng)常使用外,它也是面試官非常喜歡問(wèn)的一個(gè)知識(shí)點(diǎn)。HashMap是哈希表的一個(gè)經(jīng)典實(shí)現(xiàn),底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表,在JDK8中還引入了紅黑樹(shù),以解決鏈表線性查找的效率問(wèn)題。HashMap設(shè)計(jì)的非常優(yōu)秀,源碼兩千多行,有很多可以拿出來(lái)討論的點(diǎn),本篇文章主要分析HashMap二次哈希的目的。

2. 哈希碼的作用

首先,我們得先了解哈希碼的作用是什么?HashMap底層采用數(shù)組+鏈表/紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)鍵值對(duì)的映射關(guān)系,數(shù)組就是若干個(gè)哈希槽Solt,HashMap會(huì)通過(guò)Key算出的哈希碼來(lái)計(jì)算下標(biāo)Index,Index決定了鍵值對(duì)應(yīng)該落在哪個(gè)槽里。不同的哈希碼算出相同的下標(biāo)Index,就會(huì)導(dǎo)致哈希碰撞,一旦發(fā)生哈希碰撞,HashMap的查找效率就會(huì)從O(1)退化成O(n)或者O(logn)。所以,一個(gè)好的哈希函數(shù)應(yīng)該要盡可能的分散,否則就會(huì)影響到HashMap的效率。

3. 二次Hash

我們已經(jīng)知道,HashMap會(huì)根據(jù)哈希碼計(jì)算下標(biāo),哈希碼的分散性越好,HashMap的效率也就越高。我們先看一下HashMap計(jì)算下標(biāo)的過(guò)程,就知道它為啥要做二次Hash了。

static int indexFor(int h, int length) {
    return h & (length-1);
}

上面是HashMap根據(jù)二次Hash計(jì)算出的哈希碼,計(jì)算鍵值對(duì)下標(biāo)的代碼,length是底層數(shù)組的長(zhǎng)度。HashMap采用了位運(yùn)算,而非我們常見(jiàn)的取模運(yùn)算,這里你可以先略過(guò),它倆的效果是一樣的。

我們先來(lái)看看如果不做二次Hash的情況下,會(huì)出現(xiàn)什么問(wèn)題?,F(xiàn)在,我假設(shè)數(shù)組長(zhǎng)度為16,那么當(dāng)哈希碼為5時(shí),下標(biāo)Index結(jié)果是5。

 00000000000000000000000000000101
&00000000000000000000000000001111
=00000000000000000000000000000101
=5

當(dāng)哈希碼為65541時(shí),下標(biāo)Index結(jié)果依然是5,不同的哈希碼算出相同的下標(biāo),哈希碰撞了。

 00000000000000010000000000000101
&00000000000000000000000000001111
=00000000000000000000000000001101
=5

從這個(gè)與運(yùn)算的過(guò)程,大家肯定也都發(fā)現(xiàn)了,就是哈希碼的高位壓根就沒(méi)有參與運(yùn)算,全部被丟棄了。不管哈希碼的高位是多少,都不會(huì)影響最終Index的計(jì)算結(jié)果,因?yàn)橹挥械臀徊艆⑴c了運(yùn)算,這樣的哈希函數(shù)我們認(rèn)為是不好的,它會(huì)帶來(lái)更多的沖突,影響HashMap的效率。

如何解決這個(gè)問(wèn)題呢?最簡(jiǎn)單的辦法就是讓高位也參與到運(yùn)算,高位不一樣也會(huì)導(dǎo)致最終的Index結(jié)果不一樣,減少哈希碰撞的概率。事實(shí)上,HashMap也就是這么做的,下面是HashMap做二次Hash的源碼:

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

HashMap通過(guò)將哈希碼的高16位與低16位進(jìn)行異或運(yùn)算,得到一個(gè)新的哈希碼,這樣就可以讓高位也參與到運(yùn)算,這個(gè)函數(shù)也被稱(chēng)作「擾動(dòng)函數(shù)」。

我們用同樣的哈希碼,來(lái)看看經(jīng)過(guò)二次Hash后的哈希碼,是否會(huì)帶來(lái)不一樣的效果。
仍然假設(shè)數(shù)組長(zhǎng)度為16,那么當(dāng)哈希碼為5時(shí),下標(biāo)Index是5,結(jié)果不變。

 0000000000000101
^0000000000000000
=0000000000000101

 00000000000000000000000000000101
&00000000000000000000000000001111
=00000000000000000000000000000101
=5

當(dāng)哈希碼為65541時(shí),下標(biāo)Index結(jié)果是4,竟然沒(méi)有發(fā)生哈希碰撞。

 0000000000000101
^0000000000000001
=0000000000000100

 00000000000000010000000000000100
&00000000000000000000000000001111
=00000000000000000000000000000100
=4

可以看到,HashMap通過(guò)加入一個(gè)擾動(dòng)函數(shù),讓原本會(huì)發(fā)生碰撞的兩個(gè)哈希碼,不再?zèng)_突。

4. 為啥右移16位

HashMap的擾動(dòng)函數(shù),是拿高16位和低16位做異或運(yùn)算,把高位的特征和地位的特征組合起來(lái),以此來(lái)降低哈希碰撞的概率。為啥是16位?而不是8位或24位或其它位?

根據(jù)哈希碼計(jì)算下標(biāo)Index的過(guò)程,大家也發(fā)現(xiàn)了。實(shí)際上,只有數(shù)組長(zhǎng)度以?xún)?nèi)的低位才會(huì)參與運(yùn)算。例如數(shù)組長(zhǎng)度是16,那么只有低4位會(huì)參與計(jì)算;如果數(shù)組長(zhǎng)度是256,那么只有低8位會(huì)參與計(jì)算;如果數(shù)組長(zhǎng)度是65536,那么只有低16位會(huì)參與計(jì)算。HashMap取16位是一個(gè)折中的數(shù)字,絕大部分情況下,HashMap數(shù)組的長(zhǎng)度都不會(huì)超過(guò)65536。

5. 總結(jié)

HashMap底層采用數(shù)組+鏈表/紅黑樹(shù)來(lái)存儲(chǔ)鍵值對(duì),會(huì)根據(jù)Key的哈希碼來(lái)計(jì)算鍵值對(duì)落在數(shù)組的哪個(gè)下標(biāo)。如果不同的哈希碼算出相同的下標(biāo),就會(huì)導(dǎo)致哈希碰撞,影響HashMap的性能。HashMap要做的,就是盡量避免哈希碰撞,所以加入了擾動(dòng)函數(shù)。擾動(dòng)函數(shù)會(huì)將哈希碼的高16位與低16位做異或運(yùn)算,讓高位也參與到下標(biāo)的計(jì)算過(guò)程中來(lái),從而影響最終下標(biāo)的計(jì)算結(jié)果,減少哈希碰撞的概率。至于為啥是16位,這是因?yàn)槟男┪粫?huì)參與到下標(biāo)的計(jì)算,取決于HashMap數(shù)組的長(zhǎng)度,在絕大部分情況下,數(shù)組的長(zhǎng)度都不會(huì)超過(guò)65536,16位是一個(gè)折中的數(shù)字。

到此這篇關(guān)于深入探究HashMap二次Hash原因的文章就介紹到這了,更多相關(guān)HashMap二次Hash內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java集成presto查詢(xún)方式

    Java集成presto查詢(xún)方式

    這篇文章主要介紹了Java集成presto查詢(xún)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot全局異常處理之解決404/500錯(cuò)誤

    SpringBoot全局異常處理之解決404/500錯(cuò)誤

    在搭建項(xiàng)目框架的時(shí)候用的是springboot,想統(tǒng)一處理異常,但是發(fā)現(xiàn)404的錯(cuò)誤總是捕捉不到,總是返回的是springBoot自帶的錯(cuò)誤結(jié)果信息,這篇文章主要給大家介紹了關(guān)于SpringBoot全局異常處理之解決404/500錯(cuò)誤的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • springboot?整合表達(dá)式計(jì)算引擎?Aviator?使用示例詳解

    springboot?整合表達(dá)式計(jì)算引擎?Aviator?使用示例詳解

    本文詳細(xì)介紹了Google?Aviator?這款高性能、輕量級(jí)的?Java?表達(dá)式求值引擎,并通過(guò)詳細(xì)的代碼操作演示了相關(guān)API的使用以及如何在springboot項(xiàng)目中進(jìn)行集成,感興趣的朋友一起看看吧
    2024-08-08
  • Java?任務(wù)調(diào)度框架?Quartz實(shí)操

    Java?任務(wù)調(diào)度框架?Quartz實(shí)操

    這篇文章主要介紹了Java?任務(wù)調(diào)度框架?Quartz,Quartz是OpenSymphony開(kāi)源組織在Job?scheduling領(lǐng)域又一個(gè)開(kāi)源項(xiàng)目,完全由Java開(kāi)發(fā),可以用來(lái)執(zhí)行定時(shí)任務(wù),類(lèi)似于java.util.Timer。,下面我們來(lái)學(xué)習(xí)一下關(guān)于?Quartz更多的詳細(xì)內(nèi)容,需要的朋友可以參考一下
    2021-12-12
  • Java老手該當(dāng)心的13個(gè)錯(cuò)誤

    Java老手該當(dāng)心的13個(gè)錯(cuò)誤

    這篇文章主要介紹了Java老手該當(dāng)心的13個(gè)錯(cuò)誤,需要的朋友可以參考下
    2015-04-04
  • Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 詳解Spring3.x 升級(jí)至 Spring4.x的方法

    詳解Spring3.x 升級(jí)至 Spring4.x的方法

    本篇文章主要介紹了詳解Spring3.x 升級(jí)至 Spring4.x的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • Spring中Bean對(duì)象的定義、注冊(cè)和獲取流程分析

    Spring中Bean對(duì)象的定義、注冊(cè)和獲取流程分析

    這篇文章主要介紹了Spring中Bean對(duì)象的定義、注冊(cè)和獲取流程分析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • Java字典生成算法講解

    Java字典生成算法講解

    今天小編就為大家分享一篇關(guān)于Java字典生成算法講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • java尋找迷宮路徑的簡(jiǎn)單實(shí)現(xiàn)示例

    java尋找迷宮路徑的簡(jiǎn)單實(shí)現(xiàn)示例

    這篇文章主要介紹了java尋找迷宮路徑的簡(jiǎn)單實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評(píng)論

白朗县| 柞水县| 嘉义市| 洪洞县| 曲阜市| 曲水县| 彰化市| 湖州市| 安徽省| 德化县| 高清| 丰宁| 娄烦县| 台北市| 抚松县| 名山县| 马公市| 永新县| 平江县| 宜君县| 寻甸| 渑池县| 义乌市| 常山县| 蕲春县| 灵寿县| 西藏| 仙居县| 沛县| 儋州市| 濮阳市| 波密县| 深圳市| 会昌县| 谷城县| 尉氏县| 靖宇县| 疏附县| 巫溪县| 枝江市| 蛟河市|