點擊藍色「程序員大帝 」關注我喲java
加個「星標」,及時閱讀最新技術文章node
每日雞湯,好喝
正文
接着上篇文章《HashMap加載因子爲何是0.75?轉化紅黑樹閾值爲8?》,我們繼續聊聊 HashMap 這個重要的數據結構。雖然它很簡單,可是每一次讀源碼,我都有不一樣的體會,固然惟一不變的是對 Doug Lea 大神的崇拜。
nginx
在 JDK8 以後,對 HashMap 進行了重寫,最顯而易見的固然是引入了紅黑樹。由此而來,對它的哈希算法和尋址算法也作了必定的優化。程序員
正文
尋址算法面試
在插入和查找數據的時候,咱們會根據 key 獲得它對應的 hash 值,而後再根據這個 hash 值進行一系列計算,獲得元素在數組的下標位置,這個計算過程就是就是尋址算法。算法
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
其中最關鍵的是下面這一行,它展現瞭如何經過計算好的 hash 值來獲得對應的哈希槽的位置:數組
first = tab[(n - 1) & hash])
你們第一個想法確定是經過模運算來計算,所以引出了下面的問題
bash
HashMap 中尋址算法爲何使用&(與運算),代替模運算?微信
咱們知道了一個 key 的 hash 值,用這個hash值跟數組長度取模,就能夠獲得下標位置,其中 n 是數組的長度:數據結構
(n - 1) & hash
若是使用與運算,其實該算法的結果和模運算的結果是相同的。
可是,對於現代的處理器來講,除法和求餘數(模運算)是最慢的動做。
根據數學公式:
a % b = (b-1) & a
當 b 是 2 的指數時,等式成立。你們應該記起來,HashMap 默認的長度必定是 2 的指數冪,因此這一個等式針對 HashMap 是永遠成立的。經過與運算,提升了運算的效率。
哈希算法
static final int hash(Object key) {
int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
看完源代碼,第一次確定會被其中的異或運算和右移運算搞蒙,爲何要異或呢?爲何要移位?並且移位 16?
咱們分析一下:
首先,假設有一種狀況,若是數組長度 n=16,那麼根據尋址算法,也就是哈希值 和 15 這個數進行與運算
對象 A 的 hashCode 爲 1000 0100 0111 0001 0000 0111 1000 0000
n-1=15 0000 0000 0000 0000 0000 0000 0000 1111
對象 B 的 hashCode 爲 0111 0111 0011 1000 1010 0001 0100 0000
n-1=15 0000 0000 0000 0000 0000 0000 0000 1111
咱們會發現 A、B 和 15 這個數進行與運後,得出來的結果都是 0, 這樣的散列結果太讓人失望了。很明顯不是一個好的散列算法。
可是若是咱們將 hashCode 值右移 16 位,而後再進行異或運算(若是兩個數不一樣,結果爲1,相同爲0),這樣的話,就能避免咱們上面的狀況的發生。
對象A hashCode: 1000 0100 0111 0001 0000 0111 1000 0000
對象A hashCode右移16位: 0000 0000 0000 0000 1000 0100 0111 0001
異或運算: 1000 0100 0111 0001 1000 0011 1111 0001
n-1=15 0000 0000 0000 0000 0000 0000 0000 1111
與運算: 0000 0000 0000 0000 0000 0000 0000 0001
對象B hashCode: 0111 0111 0011 1000 1010 0001 0100 0000
對象B hashCode右移16位: 0000 0000 0000 0000 0111 0111 0011 1000
異或運算: 0111 0111 0011 1000 1101 0110 0100 1000
n-1=15 0000 0000 0000 0000 0000 0000 0000 1111
與運算: 0000 0000 0000 0000 0000 0000 0000 1000
經過結果能夠看出來,這樣異或運算獲得結果,再和 n-1 與運算,獲得結果不一樣,避免了 hash 衝突。
文末福利
找工做的小夥伴能夠後臺聯繫我,拉你進秋招/內推/面試羣,我也給你們整理了各大公司的內推通道、簡歷模板還有歷年的筆試題,你們要好好準備哦。
還能夠幫助你們免費修改簡歷、模擬面試哦~可能下期視頻的主角就是你哦~
我是無忌,Stay Tuned!
本文分享自微信公衆號 - 程序員大帝(kingcoding)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。