HashMap取模

之前看HashMap源碼時棉钧,總說HashMap數(shù)組大小要用2的n次冪吉执,取模時用到的位運算,這樣HashMap取模才會很快牲尺,也就知道了這個特性卵酪,沒有去專門了解過,為什么用2的n次冪谤碳,可以用位運算來取模溃卡;由于最近看一些框架底層代碼,位運算遇到的多了蜒简,有點好奇塑煎,就研究了下;發(fā)現(xiàn)這個取模確實很有趣臭蚁;

一最铁、(n -1) & hash 取模算法

HashMap取模算法

(n -1) & hash? 就是計算,該key是在數(shù)組中的哪個位置垮兑,下面我們研究下這個(n -1) & hash冷尉;

首先是hash這個值,轉(zhuǎn)成二進制系枪,就是0和1雀哨,我們先用個例子來說明下,n=4私爷,hash=11

11的二進制是1011

4的二進制是100

能整除的位

從上圖可以看出雾棺,因為如果用2的n次冪,當轉(zhuǎn)換成二進制后衬浑,高位肯定能整除低位(舉個例子:上面的是8捌浩,肯定能整除4,8的下一位是16工秩,肯定也能整除4尸饺,以此類推)进统,所以高位可以直接舍棄掉,只用看同樣位置后面的數(shù)浪听;后面的數(shù)就為3螟碎,但是這個3要怎么得到呢;這就是 (n - 1) 和 &的精髓了迹栓;

大家都知道绣檬,&1得到的是原來的值藕夫,高位沒有則不染庀帷猬膨;2進制數(shù)字, n-1則剛好得到的是2的n次冪小一位全是1的二進制數(shù)字答毫,所以上面的列子中,最后是計算

兩位數(shù)的&

就剛好可以直接把余數(shù)取出來季春;

看到這里了之后洗搂,突然想起來,我們創(chuàng)建HashMap時载弄,可以指定HashMap的初始大小耘拇,那我們傳進去一個不是2的n次冪的數(shù)字,那上面的就計算不了了宇攻,然后就接著研究了下惫叛,這個初始化的算法,也是相當精髓逞刷;

二嘉涌、初始化數(shù)組大小

數(shù)組大小初始化
取最高位

我們來看看JDK1.7的數(shù)組初始化,這個邏輯清晰一些夸浅;首先我們得明白這一部分邏輯肯定是為了把我們傳進來的數(shù)字仑最,給變成2的n次冪才能進行上面的取模算法;我們先看看?Integer.highestOneBit((number -1) <<1)? 這一部分帆喇;找到了highestOneBit這個方法的解釋警医,意思就是取傳進來的最高位,大概就是當傳進來為7的話坯钦,2進制為111预皇,最高位就是100,也就是4婉刀;得到的就是2的n次冪吟温;

我們先看看括號里面的是什么意思,(number -1) <<1 突颊,就是我們傳進來的數(shù)字減一溯街,然后向左移一位诱桂;開始我的理解是向左移一位就是乘以2,之后才發(fā)現(xiàn)呈昔,我們得放棄我們得數(shù)學思維挥等,不然就會想復雜了;這個向左移一位是為了取到一個大于我們傳進來的數(shù)字且剛好能滿足2的n次冪的數(shù)字堤尾,比如肝劲,我們傳進來是7,二進制就是111郭宝,那剛好大于111的2的n次冪辞槐,肯定是1000,也就是8粘室,所以榄檬,這個向左移一位,然后取最高位衔统,就剛好滿足條件鹿榜,那我們就看出這段邏輯的大概意思了;HashMap數(shù)組的長度锦爵,就是給到一個最適合的數(shù)字舱殿,并且盡量能滿足大部分的key能剛好落到數(shù)組上,不用進行鏈表的遍歷险掀,這樣取值是最快的沪袭;

看到這里,開始我一直沒明白這個?number -1 是什么意思樟氢,本來?number? << 1就可以滿足條件了冈绊,為啥還得減一,最開始的時候也懷疑過是埠啃,我們傳入的數(shù)組剛好是2的n次冪時焚碌,會有問題;其實只要一想到這個霸妹,就能明白這個意思十电,因為我們傳入8的時候,如果向左移一位的話叹螟,就是16了鹃骂,然后取最高位也是16,那就數(shù)組會比原來大一倍罢绽;但是我開始覺得畏线,如果我們能在移位之前判斷這個數(shù)字是不是2的n次冪后再進行移位,這樣是不是就不用減一了良价;然后比較下這兩種處理方式的性能寝殴,才明白蒿叠,直接減一肯定比判斷這個數(shù)字是不是2的n次冪要簡單的多,并且也快得多蚣常;并且當數(shù)字為2的n次冪時市咽,減一肯定不會使二進制數(shù)字的最高位移位,因為只有當數(shù)字為2的n次冪時抵蚊,二進制數(shù)字才是最高位是1施绎,后面全是0;其他任何情況贞绳,減一谷醉,都是修改非最高位數(shù)字,所以根本沒影響冈闭;然后到這里俱尼,就完全明白了這段代碼的邏輯,為啥HashMap性能高萎攒,因為全部是位運算遇八;

三、highestOneBit運算

那到這里就完了嗎躺酒;不押蚤,都研究到這里了蔑歌,不把highestOneBit搞明白羹应,我估計是睡不著覺了;開始看見這段邏輯我是拒絕的次屠,這矩陣都是啥啥啥啊园匹。。劫灶。算了裸违,用最原始的方式來吧;看到這個方法本昏,就發(fā)現(xiàn)供汛,都是向右移,開始移一位進行或運算涌穆,然后移兩位進行或運算怔昨,再移四位進行或運算......手動操作了下,就看明白了矮烹,我舉個極端例子吧越庇,當進入到這個方法剛好是2的n次冪,比如16搬味,運算流程如下:

highestOneBit執(zhí)行邏輯

因為最高位肯定是1问芬,我們不用管后面的位數(shù)是什么數(shù)字强戴,這個算法就是剛好能把所有位上全變成1墨微,因為第一次移一位最域,進行一次或運算,這樣,前兩位肯定都是1了翘魄,然后再把前兩位往右移兩位,然后進行或運算绩鸣,這樣前四位就都是1了,再移4位,8位垒手,16位鳖悠。。胞皱。這樣就得到了所有位上全是1的二進制數(shù)字了允蚣,然后把這個二進制數(shù)字向右移一位做入,然后減掉,就得到了只有最高位是1埠况,后面全是0的狈谊,2的n次冪數(shù)字了喜命;

四、JDK8中HashMap鏈表擴容

JDK8 HashMap鏈表擴容
JDK7 HashMap鏈表擴容

分析了上面一塊位運算,然后看到了JDK8赎瞎,如果遇到鏈表結(jié)構缓呛,遷移時攀涵,居然沒有用到取模算法去獲取該節(jié)點的位置,直接分成兩類裆操,一類直接遷入新數(shù)組的同樣索引位置怒详,一類直接遷入同樣索引加上原來數(shù)組長度的索引位置;網(wǎng)上有人說缎岗,這個是2的n次冪特性静尼,其實,這個是擴容選擇雙倍的特性;應為鼠渺,舉個例子:14%3=2? 當3擴大兩倍后 14%6=2蜗元,這是第一種情況;第二種情況系冗,就是? 17%3=2? 擴大兩倍后? 17%6=5=2+3奕扣;其實這個想的話,很容易就能想的明白掌敬;這個是數(shù)學原理惯豆,然后我們再看看代碼,這個是用位運算來代替這個原理奔害;我們回到上面(n -1) & hash這個算法的原理上楷兽,看一下就能明白為什么這個直接&就能知道,要不要加原來數(shù)組長度了华临;比如當前有兩個hash值芯杀,一個為17,一個為21雅潭,擴容前數(shù)組長度為4揭厚,擴容后為8;擴容前扶供,都落在索引為1的位置筛圆;擴容后,一個在1椿浓,一個在5太援;下面我們看下二進制計算過程;

取模的值

首先1.8的算法直接用hash和原來數(shù)組的大小直接進行&運算扳碍,運算后提岔,要么是0要么是原來數(shù)組的長度(可以用上面的例子來進行驗證下,因為是2的n次冪笋敞,所以只有一位上是1碱蒙,其他位上全是0);然后這一&運算液样,運算后振亮,就能知道擴容時巧还,原來差的那一位到底是1鞭莽,還是0;如果是1的話麸祷,得到的值肯定是大于0的澎怒,是0的話,得到的值肯定是0;然后我們再看下喷面,如果用原來的(n -1) & hash算法時星瘾,如果原來位置上是0,擴容后進行這個運算惧辈,得到的值是原值琳状;如果原來位置上是1,擴容后進行這個運算盒齿,得到的值就是念逞,數(shù)組原值最高位上,少了個1边翁,這樣轉(zhuǎn)成10進制翎承,就是剛好加上原值就行;所以這個就是為什么不用重新進行(n -1) & hash符匾,只需要 (hash&oldcap) 的原理;

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叨咖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子啊胶,更是在濱河造成了極大的恐慌甸各,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焰坪,死亡現(xiàn)場離奇詭異痴晦,居然都是意外死亡,警方通過查閱死者的電腦和手機琳彩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門誊酌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人露乏,你說我怎么就攤上這事碧浊。” “怎么了瘟仿?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵箱锐,是天一觀的道長。 經(jīng)常有香客問我劳较,道長驹止,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任观蜗,我火速辦了婚禮臊恋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘墓捻。我一直安慰自己抖仅,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撤卢,像睡著了一般环凿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上放吩,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天智听,我揣著相機與錄音,去河邊找鬼渡紫。 笑死瞭稼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的腻惠。 我是一名探鬼主播环肘,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼集灌!你這毒婦竟也來了悔雹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤欣喧,失蹤者是張志新(化名)和其女友劉穎腌零,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唆阿,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡益涧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驯鳖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闲询。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖浅辙,靈堂內(nèi)的尸體忽然破棺而出扭弧,到底是詐尸還是另有隱情,我是刑警寧澤记舆,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布鸽捻,位于F島的核電站,受9級特大地震影響泽腮,放射性物質(zhì)發(fā)生泄漏御蒲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一诊赊、第九天 我趴在偏房一處隱蔽的房頂上張望厚满。 院中可真熱鬧,春花似錦豪筝、人聲如沸痰滋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敲街。三九已至,卻和暖如春严望,著一層夾襖步出監(jiān)牢的瞬間多艇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工像吻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留峻黍,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓拨匆,卻偏偏與公主長得像姆涩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惭每,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • HashMap集合(高級) 學習地址:https://www.bilibili.com/video/BV1nJ41...
    康小莊閱讀 431評論 0 0
  • 偶然發(fā)現(xiàn)這位博主的文章很干貨骨饿,部分知識講解非常細致,尤其是開講之前的準備知識很方便小白直接理解HashMap台腥;故復...
    Divenier閱讀 378評論 0 4
  • 本文準備從以下幾個方面去講解HashMap:1)HashMap源碼詳細分析2)HashMap為什么是線程不安全的宏赘?...
    小北覓閱讀 162,096評論 28 446
  • 本文準備從以下幾個方面去講解HashMap:1)HashMap源碼詳細分析2)HashMap為什么是線程不安全的?...
    dlihasa閱讀 116評論 0 0
  • 表情是什么黎侈,我認為表情就是表現(xiàn)出來的情緒察署。表情可以傳達很多信息。高興了當然就笑了峻汉,難過就哭了贴汪。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,109評論 2 7