成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

HashMap 的 Hash 方法原理是什么?

開(kāi)發(fā) 前端
hash 方法是用來(lái)做哈希值優(yōu)化的,把哈希值右移 16 位,也就正好是自己長(zhǎng)度的一半,之后與原哈希值做異或運(yùn)算,這樣就混合了原哈希值中的高位和低位,增大了隨機(jī)性。

[[423110]]

本文 GitHub 上已同步,有 GitHub 賬號(hào)的小伙伴,記得看完后給二哥安排一波 star 呀!沖一波 GitHub 的 trending 榜單,求求各位了。

GitHub 地址:https://github.com/itwanger/toBeBetterJavaer

在線(xiàn)閱讀地址:https://itwanger.gitee.io/tobebetterjavaer

來(lái)看一下 hash 方法的源碼(JDK 8 中的 HashMap):

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

這段代碼究竟是用來(lái)干嘛的呢?

我們都知道,key.hashCode() 是用來(lái)獲取鍵位的哈希值的,理論上,哈希值是一個(gè) int 類(lèi)型,范圍從-2147483648 到 2147483648。前后加起來(lái)大概 40 億的映射空間,只要哈希值映射得比較均勻松散,一般是不會(huì)出現(xiàn)哈希碰撞的。

但問(wèn)題是一個(gè) 40 億長(zhǎng)度的數(shù)組,內(nèi)存是放不下的。HashMap 擴(kuò)容之前的數(shù)組初始大小只有 16,所以這個(gè)哈希值是不能直接拿來(lái)用的,用之前要和數(shù)組的長(zhǎng)度做取模運(yùn)算,用得到的余數(shù)來(lái)訪(fǎng)問(wèn)數(shù)組下標(biāo)才行。

取模運(yùn)算有兩處。

取模運(yùn)算(“Modulo Operation”)和取余運(yùn)算(“Remainder Operation ”)兩個(gè)概念有重疊的部分但又不完全一致。主要的區(qū)別在于對(duì)負(fù)整數(shù)進(jìn)行除法運(yùn)算時(shí)操作不同。取模主要是用于計(jì)算機(jī)術(shù)語(yǔ)中,取余則更多是數(shù)學(xué)概念。

一處是往 HashMap 中 put 的時(shí)候(putVal 方法中):

  1. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { 
  2.      HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i; 
  3.      if ((tab = table) == null || (n = tab.length) == 0) 
  4.          n = (tab = resize()).length; 
  5.      if ((p = tab[i = (n - 1) & hash]) == null
  6.          tab[i] = newNode(hash, key, value, null); 

一處是從 HashMap 中 get 的時(shí)候(getNode 方法中):

  1. final Node<K,V> getNode(int hash, Object key) { 
  2.      Node<K,V>[] tab; Node<K,V> first, e; int n; K k; 
  3.      if ((tab = table) != null && (n = tab.length) > 0 && 
  4.             (first = tab[(n - 1) & hash]) != null) {} 

其中的 (n - 1) & hash 正是取模運(yùn)算,就是把哈希值和(數(shù)組長(zhǎng)度-1)做了一個(gè)“與”運(yùn)算。

可能大家在疑惑:取模運(yùn)算難道不該用 % 嗎?為什么要用 & 呢?

這是因?yàn)?& 運(yùn)算比 % 更加高效,并且當(dāng) b 為 2 的 n 次方時(shí),存在下面這樣一個(gè)公式。

  1. a % b = a & (b-1) 

用 替換下 b 就是:

我們來(lái)驗(yàn)證一下,假如 a = 14,b = 8,也就是 ,n=3。

這也正好解釋了為什么 HashMap 的數(shù)組長(zhǎng)度要取 2 的整次方。

因?yàn)?數(shù)組長(zhǎng)度-1)正好相當(dāng)于一個(gè)“低位掩碼”——這個(gè)掩碼的低位最好全是 1,這樣 & 操作才有意義,否則結(jié)果就肯定是 0,那么 & 操作就沒(méi)有意義了。

a&b 操作的結(jié)果是:a、b 中對(duì)應(yīng)位同時(shí)為 1,則對(duì)應(yīng)結(jié)果位為 1,否則為 0

2 的整次冪剛好是偶數(shù),偶數(shù)-1 是奇數(shù),奇數(shù)的二進(jìn)制最后一位是 1,保證了 hash &(length-1) 的最后一位可能為 0,也可能為 1(這取決于 h 的值),即 & 運(yùn)算后的結(jié)果可能為偶數(shù),也可能為奇數(shù),這樣便可以保證哈希值的均勻性。

& 操作的結(jié)果就是將哈希值的高位全部歸零,只保留低位值,用來(lái)做數(shù)組下標(biāo)訪(fǎng)問(wèn)。

假設(shè)某哈希值為 10100101 11000100 00100101,用它來(lái)做取模運(yùn)算,我們來(lái)看一下結(jié)果。HashMap 的初始長(zhǎng)度為 16(內(nèi)部是數(shù)組),16-1=15,二進(jìn)制是 00000000 00000000 00001111(高位用 0 來(lái)補(bǔ)齊):

  1.   10100101 11000100 00100101 
  2. & 00000000 00000000 00001111 
  3. ---------------------------------- 
  4.   00000000 00000000 00000101 

因?yàn)?15 的高位全部是 0,所以 & 運(yùn)算后的高位結(jié)果肯定是 0,只剩下 4 個(gè)低位 0101,也就是十進(jìn)制的 5,也就是將哈希值為 10100101 11000100 00100101 的鍵放在數(shù)組的第 5 位。

明白了取模運(yùn)算后,我們?cè)賮?lái)看 put 方法的源碼:

  1. public V put(K key, V value) { 
  2.     return putVal(hash(key), key, value, falsetrue); 

以及 get 方法的源碼:

  1. public V get(Object key) { 
  2.     HashMap.Node<K,V> e; 
  3.     return (e = getNode(hash(key), key)) == null ? null : e.value; 

它們?cè)谡{(diào)用 putVal 和 getNode 之前,都會(huì)先調(diào)用 hash 方法:

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

那為什么取模運(yùn)算之前要調(diào)用 hash 方法呢?

看下面這個(gè)圖。

某哈希值為 11111111 11111111 11110000 1110 1010,將它右移 16 位(h >>> 16),剛好是 00000000 00000000 11111111 11111111,再進(jìn)行異或操作(h ^ (h >>> 16)),結(jié)果是 11111111 11111111 00001111 00010101

異或(^)運(yùn)算是基于二進(jìn)制的位運(yùn)算,采用符號(hào) XOR 或者^(guò)來(lái)表示,運(yùn)算規(guī)則是:如果是同值取 0、異值取 1

由于混合了原來(lái)哈希值的高位和低位,所以低位的隨機(jī)性加大了(摻雜了部分高位的特征,高位的信息也得到了保留)。

結(jié)果再與數(shù)組長(zhǎng)度-1(00000000 00000000 00000000 00001111)做取模運(yùn)算,得到的下標(biāo)就是 00000000 00000000 00000000 00000101,也就是 5。

還記得之前我們假設(shè)的某哈希值 10100101 11000100 00100101 嗎?在沒(méi)有調(diào)用 hash 方法之前,與 15 做取模運(yùn)算后的結(jié)果也是 5,我們不妨來(lái)看看調(diào)用 hash 之后的取模運(yùn)算結(jié)果是多少。

某哈希值 00000000 10100101 11000100 00100101(補(bǔ)齊 32 位),將它右移 16 位(h >>> 16),剛好是 00000000 00000000 00000000 10100101,再進(jìn)行異或操作(h ^ (h >>> 16)),結(jié)果是 00000000 10100101 00111011 10000000

結(jié)果再與數(shù)組長(zhǎng)度-1(00000000 00000000 00000000 00001111)做取模運(yùn)算,得到的下標(biāo)就是 00000000 00000000 00000000 00000000,也就是 0。

綜上所述,hash 方法是用來(lái)做哈希值優(yōu)化的,把哈希值右移 16 位,也就正好是自己長(zhǎng)度的一半,之后與原哈希值做異或運(yùn)算,這樣就混合了原哈希值中的高位和低位,增大了隨機(jī)性。

說(shuō)白了,hash 方法就是為了增加隨機(jī)性,讓數(shù)據(jù)元素更加均衡的分布,減少碰撞。

參考鏈接:

 

https://blog.csdn.net/lonyw/article/details/80519652 >https://zhuanlan.zhihu.com/p/91636401 >https://www.zhihu.com/question/20733617

 

責(zé)任編輯:武曉燕 來(lái)源: 沉默王二
相關(guān)推薦

2023-02-13 08:01:49

HashHashMapint

2025-01-15 13:30:48

FeignHTTPJava

2023-11-05 10:52:54

DNS服務(wù)器瀏覽器

2024-11-25 12:20:00

Hystrix微服務(wù)架構(gòu)

2021-04-26 07:51:00

JavaScript方法函數(shù)

2016-09-12 14:33:20

javaHashMap

2023-07-11 08:00:00

2012-03-15 16:27:13

JavaHashMap

2023-01-04 07:54:03

HashMap底層JDK

2021-09-27 08:02:17

CDN加速網(wǎng)站網(wǎng)絡(luò)

2021-05-09 09:30:13

Docker操作系統(tǒng)容器

2021-07-29 11:46:27

NAS存儲(chǔ)NAS服務(wù)器

2013-06-06 13:10:44

HashMap無(wú)鎖

2023-10-18 10:55:55

HashMap

2018-02-23 14:13:39

前端Cookie用戶(hù)登錄

2022-02-25 14:11:48

短網(wǎng)址Java算法

2024-12-24 14:11:57

2017-07-13 10:43:52

CNNmaxpool池化

2024-06-06 08:53:13

動(dòng)態(tài)鏈接庫(kù)共享庫(kù)

2020-03-23 10:09:27

云安全云計(jì)算
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 久视频在线 | 亚洲精品久久久一区二区三区 | 国产美女精品视频 | 99精品免费久久久久久日本 | 国产日韩精品一区 | 一区二区三区国产 | 一区二区三区精品在线视频 | 国外成人在线视频 | 日韩av一区二区在线观看 | 亚洲精品日韩精品 | 亚洲精品视频三区 | 51ⅴ精品国产91久久久久久 | 国产精品久久久久久久久久久免费看 | 国产精品久久久久一区二区三区 | 久久人| 91精品国产综合久久久久久首页 | 亚洲欧美一区二区三区1000 | 国产最新视频在线 | 成人欧美一区二区三区在线观看 | 国产成人精品一区二区 | 欧美 日韩 中文 | 男女视频网站 | 九热在线 | 91成人在线视频 | 美女天天干 | 一区二区三区在线播放视频 | 狠狠久久| 亚洲国产视频一区二区 | 日韩免费 | 免费成人国产 | 欧美日韩精品一区 | 免费观看羞羞视频网站 | 黑人精品欧美一区二区蜜桃 | 欧美一区视频 | 午夜精品一区二区三区在线视 | 亚洲第一中文字幕 | 成人在线视频观看 | 欧美在线观看一区二区 | 成人精品视频99在线观看免费 | 成人一区二区在线 | 欧美性生交大片免费 |