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


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內存模型是兩個概念。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM