HashMap的初始容量
背景
很多人可以把HashMap的原理描述的很溜。比如JDK1.7之前,底層數據結構是數組+鏈表。JDK1.8之后,出於效率上的考慮,在數組長度大於64,鏈表長度大於8的時候,會轉換為紅黑樹。
甚至知道對於賦值了容量的都會做一個變成2的n次方的操作。它的hash方法為了防止高位變化大或者低位變化大將它本身hash值右移16位和自身原hash值做一個按位異或操作再與容量-1做按位與。還知道默認的負載因子是0.75,這個值是經過概率論統計出來的,最好不要改。
了解的這么清楚,我就想問一下為什么從數據庫中取出來一個list,之后轉換成hashmap。直接用的是Map map = new HashMap()或者是Map map = Maps.newHashMap(),為什么不賦初始容量呢?
分析
容量的大小會在put過程中發生resize操作。如果初始不賦值。默認容量是16。那比如從數據庫中取出來1000個元素。put過程中會從16->32->64->128……,運行多次resize操作。resize操作數組,需要將所有元素進行復制和rehash,效率是很低的。
所以也有一些同學考慮到這個問題,代碼是這么寫的: Map map = new HashMap(list.size());
這個寫法也有問題,因為resize並不是到達容量上限才resize。為了盡量避免hash沖突,是超過閾值threshold就擴容。而這個threshold=容量*負載因子。
所以我更建議的寫法是Map map = new HashMap(list.size()/負載因子)。
這樣理論上可以比Map map = new HashMap(list.size())減少一次resize。
總結
在可以確定HashMap容量時,最好Map map = new HashMap(list.size()/負載因子)來初始化,避免自動擴容帶來的性能損耗。
思考
ConcurrentHashMap怎么來更合理的初始化?
JVM內存結構和Java內存模型
背景
前段時間偶然看到有篇文章批判很多人對「JVM內存模型」這個概念不清楚,說這個經典的圖並不是內存模型而是內存結構。
而內存模型應該是JSR133規范里介紹的volatile、final和synchronized等關鍵字的內存語義。
分析
這個非常富有淘金式思維的作者卻搞混了一個概念,看看下面JSR-133規范里是怎么說的:JSR133規范里講的Java內存模型,並沒有說是JVM的內存模型啊。
Java內存模型講的是Java語言本身的規范,這個規范包含了各個Java標准關鍵字在JVM里是怎樣運作的。而JVM內存模型描述的是Java虛擬機怎樣運行字節碼的。所以上面經典的圖說是JVM內存模型也不為過。不過根據官網,叫JVM內存結構更為標准。證據如下:
https://docs.oracle.com/javase/specs/jvms/se14/html/jvms-2.html#jvms-2.5
在oracle官網里,介紹了這個概念
總結
Java內存模型和JVM內存模型是兩個概念。