JDK 1.8 中hash算法和尋找算法是如何優化?
// JDK 1.8以后的HashMap里面的一段源碼
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
高低16位參與運算:
比如有一個key的hash值
原值: 1111 1111 1111 1111 1111 1010 0111 1100
右移16位:0000 0000 0000 0000 1111 1111 1111 1111
異或運算: 1111 1111 1111 1111 0000 0101 1000 0011 -> 轉換成int值表示hash值
尋址算法優化:
(n - 1) & hash -> 算出數組里的一個位置下標
取模運算性能差一些,為了優化數組尋址過程,數組長度2的n次方,hash & (n – 1)效果跟hash對n取模效果一樣,與運算性能更高。
核心在於低16的與運算
hash算法的優化:對每個hash值,在他的低16位中,讓高低16位進行了異或,讓他的低16位同時保持了高低16位的特征,盡量避免一些hash值后續出現沖突,大家可能會進入數組的同一個位置。
HashMap如何解決hash碰撞問題?
hash沖突問題,鏈表+紅黑樹,O(n)和O(logn)
map.put和map.get -> hash算法優化(避免hash沖突),尋址性能優化
假設你的鏈表很長,可能會導致遍歷鏈表,性能會比較差,O(n)
優化,如果鏈表的長度達到了一定的長度之后,其實會把鏈表轉換為紅黑樹,遍歷一顆紅黑樹找一個元素,此時O(logn),性能會比鏈表高一些。
說說HashMap是如何進行擴容的?
2的N次方擴容。16,32,64。。。
rehash:
如果數組的長度擴容之后 = 32,重新對每個hash值進行尋址,也就是用每個hash值跟新數組的length - 1進行與操作。
n-1 0000 0000 0000 0000 0000 0000 0001 1111
hash1 1111 1111 1111 1111 0000 1111 0000 0101
&結果 0000 0000 0000 0000 0000 0000 0000 0101 = 5(index = 5的位置)
n-1 0000 0000 0000 0000 0000 0000 0001 1111
hash2 1111 1111 1111 1111 0000 1111 0001 0101
&結果 0000 0000 0000 0000 0000 0000 0001 0101 = 21(index = 21的位置)
判斷二進制結果中是否多出一個bit的1,如果沒多,那么就是原來的index,如果多了出來,那么就是index + oldCap,通過這個方式,就避免了rehash的時候,用每個hash對新數組.length取模,取模性能不高,位運算的性能比較高。
參考資料:
互聯網Java工程師面試突擊(第三季)-- 中華石杉