背景:hashmap面試基礎必考內容,需要深入了解,並學習其中的相關原理。此處還要明白1.7和1.8不通版本的優化點。
Java 8系列之重新認識HashMap
鑒於JDK1.8做了多方面的優化,總體性能優於JDK1.7,下面我們從兩個方面用例子證明這一點(在hash均勻和不均勻的情況下性能都有明顯的提升)
不管增加、刪除、查找鍵值對,定位到哈希桶數組的位置都是很關鍵的第一步。前面說過HashMap的數據結構是數組和鏈表的結合,所以我們當然希望這個HashMap里面的元素位置盡量分布均勻些,盡量使得每個位置上的元素數量只有一個,那么當我們用hash算法求得這個位置的時候,馬上就可以知道對應位置的元素就是我們要的,不用遍歷鏈表,大大優化了查詢的效率。HashMap定位數組索引位置,直接決定了hash方法的離散性能。先看看源碼的實現(方法一+方法二):
方法一: static final int hash(Object key) { //jdk1.8 & jdk1.7 int h; // h = key.hashCode() 為第一步 取hashCode值 // h ^ (h >>> 16) 為第二步 高位參與運算 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } 方法二: static int indexFor(int h, int length) { //jdk1.7的源碼,jdk1.8沒有這個方法,但是實現原理一樣的 return h & (length-1); //第三步 取模運算 }
這里的Hash算法本質上就是三步:取key的hashCode值、高位運算、取模運算。
對於任意給定的對象,只要它的hashCode()返回值相同,那么程序調用方法一所計算得到的Hash碼值總是相同的。我們首先想到的就是把hash值對數組長度取模運算,這樣一來,元素的分布相對來說是比較均勻的。但是,模運算的消耗還是比較大的,在HashMap中是這樣做的:調用方法二來計算該對象應該保存在table數組的哪個索引處。
這個方法非常巧妙,它通過h & (table.length -1)來得到該對象的保存位,而HashMap底層數組的長度總是2的n次方,這是HashMap在速度上的優化。當length總是2的n次方時,h& (length-1)運算等價於對length取模,也就是h%length,但是&比%具有更高的效率。
在JDK1.8的實現中,優化了高位運算的算法,通過hashCode()的高16位異或低16位實現的:(h = k.hashCode()) ^ (h >>> 16),主要是從速度、功效、質量來考慮的,這么做可以在數組table的length比較小的時候,也能保證考慮到高低Bit都參與到Hash的計算中,同時不會有太大的開銷。
Java源碼分析:HashMap 1.8 相對於1.7 到底更新了什么?
Java源碼分析:HashMap 1.8 相對於1.7 到底更新了什么?
ps超詳細的 太牛逼了 好好讀讀
1.8不會出現1.7的多線程環形鏈死循環情況,但是還不是線程安全的,因為沒有同步鎖。
jdk1.8與1.7之間 hashmap的不同,優化點!!
所有處理的根本目的,都是為了提高 存儲key-value
的數組下標位置 的隨機性 & 分布均勻性,盡量避免出現hash值沖突。即:對於不同key
,存儲的數組下標位置要盡可能不一樣
JDK 1.8
的優化目的主要是:減少 Hash
沖突 & 提高哈希表的存、取效率