關於Java兩點需要更新的知識

程式設計一生發表於2020-05-27

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記憶體模型是兩個概念。

相關文章