亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

HashMap確定key的存儲(chǔ)位置的源碼分析

 更新時(shí)間:2023年07月24日 10:42:48   作者:?jiǎn)纬誊嚻? 
HashMap 作為 Java 中最常用的數(shù)據(jù)結(jié)構(gòu)之一,用于存儲(chǔ)和管理鍵值對(duì),HashMap 基于哈希函數(shù)實(shí)現(xiàn),能通過(guò)將 key 映射到特定的位置來(lái)實(shí)現(xiàn)快速存儲(chǔ)、查找和刪除數(shù)據(jù),接下來(lái)將從源碼角度分析以通俗易懂的方式向大家講解一下 HashMap 如何確定 key 的存儲(chǔ)位置的

前言

HashMap 通過(guò)哈希函數(shù)確定 key 的存儲(chǔ)位置這一映射過(guò)程,可使得 HashMap 在常數(shù)時(shí)間內(nèi)完成插入、查找和刪除操作,那么大家是否有了解過(guò) HashMap 底層是如何使用哈希函數(shù)實(shí)現(xiàn)映射的呢?是如何高效確認(rèn) key 的存儲(chǔ)位置的呢?

接下來(lái)將從源碼角度分析以通俗易懂的方式向大家講解一下 HashMap 如何確定 key 的存儲(chǔ)位置的。

源碼分析

下面以 JDK 1.8 版本查看 HashMap 中最為常用的方法之一 put(K key, V value) 方法來(lái)看看 key 是如何確定存儲(chǔ)位置的。

可以看到這里 key 會(huì)先通過(guò) hash() 方法進(jìn)行處理,進(jìn)入 hash() 方法一探究竟。

擾動(dòng)函數(shù) #hash()

JDK 1.8 中的 hash() 方法

源碼:

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

第一次看到 (h = key.hashCode()) ^ (h >>> 16) 這個(gè)式子可能有點(diǎn)不太好理解,我們分開來(lái)看:

  • 可以看到式子中的 key.hashCode(),說(shuō)明 HashMap 會(huì)將傳入的 key 調(diào)用自身父類 ObjecthashCode() 來(lái)進(jìn)行哈希計(jì)算。
  • 然后將計(jì)算得到的哈希值 h 通過(guò) h ^ (h >>> 16) 把高 16 位異或(^)低 16 位形成新的哈希值(hashCode() 方法返回的是 int 類型,也就是 32 位的數(shù)據(jù))。

(h = "key".hashCode() ^ (h >>> 16)) 為例:

將計(jì)算后的哈希值的高 16 位與低 16 位異或(^)的目的是為了通過(guò)把原本的低 16 位變成高 16 位和低 16 位的混合結(jié)果來(lái)使得低 16 位的隨機(jī)性增大,這樣在數(shù)組長(zhǎng)度較小時(shí),能起到保證高 16 位也參與到 Hash 計(jì)算中(這個(gè)在后面的取模操作中很好的體現(xiàn)),同時(shí)不會(huì)造成太大的性能開銷。

JDK1.8 中 HashMap 的 hash() 方法也叫做擾動(dòng)函數(shù),其目的就是為了增加隨機(jī)性,讓數(shù)據(jù)元素更加均衡的散列,減少碰撞。

補(bǔ)充 JDK 1.7 的 hash() 方法

static int hash(int h) {
    int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
    h ^= k.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12); 
    return h ^ (h >>> 7) ^ (h >>> 4);
}

JDK 1.7 中對(duì) hashCode() 方法計(jì)算出的哈希值進(jìn)行了四次擾動(dòng),所以在性能上要差一點(diǎn)。在 JDK 1.8 對(duì)其優(yōu)化了擾動(dòng)算法,使得計(jì)算的性能開銷降低許多。這里也是 JDK 1.7 和 1.8 在計(jì)算 key 的存儲(chǔ)位置上不同的地方了。

取模操作

在擾動(dòng)函數(shù) hash() 方法中可以看到返回的是一個(gè) int 類型的值,即值的范圍在 [-2147483648, 2147483647] 內(nèi),也就是說(shuō)通過(guò) hash() 映射到的位置可以在近 40 億的長(zhǎng)度的數(shù)組中。

但是實(shí)際上的內(nèi)存不允許數(shù)組達(dá)到這么大,那么 HashMap 是如何將這個(gè)哈希值映射到數(shù)組中呢,相信讀者們的第一反應(yīng)一定是取模了,那么 HashMap 是怎樣進(jìn)行取模操作的呢,深入 putVal() 方法可以看到:

可以看到方法中通過(guò) (n - 1) & hash 來(lái)確定最終 key 的存儲(chǔ)位置,其實(shí) (n - 1) & hash 這個(gè)式子就是取模操作,把通過(guò) hash() 方法計(jì)算后得到的 hash 和當(dāng)前 HashMap 的數(shù)組長(zhǎng)度 - 1 進(jìn)行與運(yùn)算,相當(dāng)于 hash % n,從而將其映射到數(shù)組特定的索引中。那么為什么 (n - 1) & hash 等價(jià)于 hash % n 呢?

其實(shí)這個(gè)等價(jià)關(guān)系有一個(gè)必要的前提,這個(gè)前提就是 n 總是 2n2^n2n,即數(shù)組的長(zhǎng)度總是 2 的冪次方。當(dāng)數(shù)組長(zhǎng)度為 2 的冪次方時(shí),可以保證 (n - 1) & hash 等價(jià)于 hash % n。

這是因?yàn)楫?dāng) n 為 2 的冪次方時(shí),n - 1 可以保證高位為 0,低位全 1 的效果,所以,按位與運(yùn)算的結(jié)果相當(dāng)于保留了 hash 的二進(jìn)制表示的低位部分,而將高位部分全部置為 0,從而確保了 (n - 1) & hash 等價(jià)于 hash % n。

舉個(gè)例子:上圖可以知道 "key" 的哈希值計(jì)算結(jié)果為 0000 0000 0000 0001 1001 1110 0101 1110 即 106078,假設(shè)當(dāng)前數(shù)組長(zhǎng)度為 16 即 0001 0000,那么正常取模為 106078 % 16 = 14。

106078 & (16 - 1) 結(jié)果如圖:

至于為什么要使用位運(yùn)算呢?這是因?yàn)槿∧_\(yùn)算的性能開銷較大,替換成位運(yùn)算可以得到更高的計(jì)算效率,提高性能。并且數(shù)組長(zhǎng)度為 2 的冪次方可以起到散列均勻分布,減少哈希碰撞的可能性。

總結(jié)

上面就是 HashMap 如何確定 key 的存儲(chǔ)位置的整個(gè)源碼分析過(guò)程了,過(guò)程可以分為三步:

  • 將傳入的參數(shù) key 調(diào)用自身的方法 hashCode() 得到哈希值 h。
  • 根據(jù)哈希值 h 調(diào)用擾動(dòng)函數(shù) hash() 計(jì)算 h ^ (h >>> 16) 得到擾動(dòng)后的哈希值 hash。
  • 根據(jù)哈希值 hash 取模操作 hash & (n - 1) 從而確定 key 的存儲(chǔ)位置。

以上就是本篇文章的全部?jī)?nèi)容了。

到此這篇關(guān)于HashMap確定key的存儲(chǔ)位置的源碼分析的文章就介紹到這了,更多相關(guān)HashMap確定key位置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java 獲取路徑的各種方法(總結(jié))

    java 獲取路徑的各種方法(總結(jié))

    下面小編就為大家?guī)?lái)一篇java 獲取路徑的各種方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • 如何處理@PathVariable中的特殊字符問(wèn)題

    如何處理@PathVariable中的特殊字符問(wèn)題

    這篇文章主要介紹了如何處理@PathVariable中的特殊字符問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • java實(shí)現(xiàn)選課系統(tǒng)

    java實(shí)現(xiàn)選課系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)選課系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • java中public class與class的區(qū)別詳解

    java中public class與class的區(qū)別詳解

    以下是對(duì)java中public class與class的區(qū)別進(jìn)行了分析介紹,需要的朋友可以過(guò)來(lái)參考下
    2013-07-07
  • Java full gc觸發(fā)情況實(shí)例解析

    Java full gc觸發(fā)情況實(shí)例解析

    這篇文章主要介紹了Java full gc觸發(fā)情況實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • JFrame中添加和設(shè)置JPanel的方法實(shí)例解析

    JFrame中添加和設(shè)置JPanel的方法實(shí)例解析

    這篇文章主要介紹了JFrame中添加和設(shè)置JPanel的方法實(shí)例解析,具有一定借鑒價(jià)值
    2018-01-01
  • Spring的CorsFilter會(huì)失效的原因及解決方法

    Spring的CorsFilter會(huì)失效的原因及解決方法

    眾所周知CorsFilter是Spring提供的跨域過(guò)濾器,我們可能會(huì)做以下的配置,基本上就是允許任何跨域請(qǐng)求,我利用Spring的CorsFilter做跨域操作但是出現(xiàn)報(bào)錯(cuò),接下來(lái)小編就給大家介紹一Spring的CorsFilter會(huì)失效的原因及解決方法,需要的朋友可以參考下
    2023-09-09
  • 深入解讀 Spring Boot 生態(tài)之功能、組件與優(yōu)勢(shì)

    深入解讀 Spring Boot 生態(tài)之功能、組件與優(yōu)勢(shì)

    本文將深入剖析 Spring Boot 的生態(tài)體系,包括其核心功能、生態(tài)組件以及在不同場(chǎng)景中的應(yīng)用,并附上一張 Spring Boot 生態(tài)系統(tǒng)圖,幫助開發(fā)者更直觀地理解 Spring Boot 的強(qiáng)大之處,感興趣的朋友一起看看吧
    2024-11-11
  • mybatisPlus返回Map類型的集合

    mybatisPlus返回Map類型的集合

    本文主要介紹了mybatisPlus返回Map類型的集合,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 說(shuō)明Java的傳遞與回調(diào)機(jī)制的代碼示例分享

    說(shuō)明Java的傳遞與回調(diào)機(jī)制的代碼示例分享

    這篇文章主要介紹了說(shuō)明Java的傳遞與回調(diào)機(jī)制的代碼示例分享,傳遞與回調(diào)機(jī)制是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09

最新評(píng)論