深入了解Java的哈希圖和Concurrenthashmap
Hashmap不是線程安全的,僅應在單線程環境或外部同步中使用,而ConsurrentHashMap是線程安全的,並且設計用於並發訪問。 2。哈希圖允許null鍵和值,而如果使用null,則conurenthashmap會拋出NullPoInterException。 3。哈希圖使用失敗的迭代器,在迭代過程中對結構修飾進行consturrentModification Exception,但ConturrentHashMap提供了弱一致的迭代器,這些迭代器不會引發此例外。 4。在內部,哈希圖依賴於數組鏈接的列表/樹結構而沒有鎖定,而ConcurrentHashmap則使用細顆粒的存儲桶級同步和CAS操作進行線程安全。 5。 ConcurrentHashMap在多線程方案中提供了更好的可擴展性,這是由於其無鎖的讀取操作和諸如Computeifabsent之類的原子更新方法。 6。使用hashmap進行單線程性能和空支持;使用ConcurRentHashMap用於線程安全,高電匯應用程序,例如緩存或共享狀態管理。
Java的HashMap
和ConcurrentHashMap
是Java應用程序中使用最廣泛的MAP實現。儘管它們具有相似的目的(存儲鑰匙值對,但它們的內部工作,性能特徵和線程安全差異很大。在構建可擴展和可靠的應用程序時,了解這些差異至關重要。

讓我們深入研究兩者,從HashMap
開始,然後轉到ConcurrentHashMap
,最後在關鍵領域進行比較。
1。哈希圖:簡單性和速度(但不是線程安全)
HashMap
是Java Collections框架的一部分,並提供了基本的哈希表實現。它允許null
鍵和值,並為在理想條件下get
和put
操作提供平均O(1)時間複雜性。

它如何在內部運作
數組鏈接列表/樹結構:
HashMap
使用Node
(或Entry
)對象的數組。每個節點都有一個鍵值對,哈希和對下一個節點的引用(用於碰撞處理)。-
哈希:
鍵的hashCode()
用於計算內部數組中的索引。如果兩個鍵具有相同的哈希(碰撞),則使用鏈接列表將它們存儲在同一存儲桶中。 碰撞處理:
當一個水桶生長到閾值之上(默認8)並且表格足夠大時,鏈接列表將轉換為平衡的樹(紅色黑樹),以將查找時間從O(N)減少到O(log n)。調整大小:
當條目數量超過負載因子(默認為0.75)×容量時,地圖大小,將存儲桶陣列增加一倍並重新調整所有條目。這很昂貴,可能會導致停頓。
關鍵限制
- 不是線程安全:並發修改會導致比賽條件。
-
失敗迭代器:如果在迭代期間對映射進行了結構修改(除了通過
Iterator.remove()
),則拋出了ConcurrentModificationException
。 - 在高爭議下的性能差:多個線程在沒有外部同步的情況下訪問它會破壞內部結構(例如,在調整較舊版本的大小期間,無限循環)。
⚠️在多線程環境中切勿在沒有同步的情況下使用
HashMap
。
2。 concurrenthashmap:線程安全和可擴展
在Java 5中引入,並在Java 8中顯著改進, ConcurrentHashMap
旨在並發訪問。它提供了線程安全性,而無需鎖定整個地圖,該地圖允許多個讀取器和並發更新。
跨Java版本的演變
- Java 7 :使用的分段鎖定- 地圖分為段,每個片段都有自己的鎖定。這允許並發在不同的細分市場中寫入。
- Java 8 :使用單個存儲桶和CAS(比較與S-wap)操作的
synchronized
塊用細粒度鎖定替換片段。這種提高了可伸縮性和降低的爭議。
它如何實現並發
- 鎖條(Java Pre-Java 8) :多個鎖保護地圖的不同部分。
- 節點級同步(Java 8) :在寫操作過程中,每個存儲桶都可以獨立鎖定。
- CAS操作:用於原子更新(例如,在存儲桶中插入第一個節點)。
-
樹垃圾箱:就像
HashMap
一樣,水桶可以在高碰撞下變成樹。
線程安全操作
- 所有操作(
get
,put
,remove
,compute
等)均為線程安全。 - 無需外部同步。
- 迭代器不會拋出
ConcurrentModificationException
,它們在某個時間點(弱一致)反映了地圖的狀態。
有用的並發方法
Java 8添加了在並發下安全的功能風格的方法:
-
computeIfAbsent(key, mappingFunction)
-
merge(key, value, remappingFunction)
-
forEach(BiConsumer)
這些在緩存方案中特別有用:
cache.computeifabsent(“ key”,k-> expensiveCompont());
3。哈希圖vs concurrenthashmap:關鍵差異
特徵 | HashMap | ConcurrentHashMap |
---|---|---|
線程安全 | ❌不 | ✅是的 |
同步 | 手冊(例如, Collections.synchronizedMap() ) | 內建 |
空鍵/值 | ✅允許 | ❌不允許(投擲NullPointerException ) |
表現 | 在單線程上下文中快速 | 由於開銷而稍慢一些,但在並發下縮放效果更好 |
迭代 | 失敗 | 弱一致(沒有ConcurrentModificationException ) |
內部鎖定 | 沒有任何 | 水桶級( synchronized )CAS |
最好的用例 | 單線程或外部同步方案 | 具有高讀/寫並發的多線程環境 |
4。什麼時候使用?
✅使用HashMap
時:
- 您處於單線程上下文。
- 您需要存儲
null
鍵或值。 - 您正在構建一張本地短壽命的地圖,而不是跨線程共享。
- 績效至關重要,並發並不關心。
✅使用ConcurrentHashMap
時:
- 多個線程同時讀寫。
- 您需要高吞吐量而無需外部同步。
- 您正在實施緩存,註冊表或共享狀態。
- 您想使用
computeIfAbsent
等原子操作。
?不要這樣做:
同步(地圖){ 如果(!map.containskey(key)){ map.put(鍵,值); } }這容易出錯,效率低下。而是使用:
concurrentMap.putifabsent(鍵,value);
最後的想法
HashMap
簡單,快速且非常適合非交通使用。ConcurrentHashMap
是線程安全地圖的首選選擇,在不犧牲太多性能的情況下提供了高的並發和安全性。- Java 8中的內部重新設計通過消除片段鎖並包含CAS和細粒度同步,從而提高了
ConcurrentHashMap
施加武器。在更深層次的層面上了解這些地圖有助於您做出更好的設計決策,尤其是在構建高性能,並發的Java應用程序時。
基本上,如果您要跨線程共享地圖,只需使用
ConcurrentHashMap
即可。這不是魔術,但很接近。以上是深入了解Java的哈希圖和Concurrenthashmap的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undress AI Tool
免費脫衣圖片

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

HashMap在Java中通過哈希表實現鍵值對存儲,其核心在於快速定位數據位置。 1.首先使用鍵的hashCode()方法生成哈希值,並通過位運算轉換為數組索引;2.不同對象可能產生相同哈希值,導致衝突,此時以鍊錶形式掛載節點,JDK8後鍊錶過長(默認長度8)則轉為紅黑樹提升效率;3.使用自定義類作鍵時必須重寫equals()和hashCode()方法;4.HashMap動態擴容,當元素數超過容量乘以負載因子(默認0.75)時,擴容並重新哈希;5.HashMap非線程安全,多線程下應使用Concu

Optional能清晰表達意圖並減少null判斷的代碼噪音。 1.Optional.ofNullable是處理可能為null對象的常用方式,如從map中取值時可結合orElse提供默認值,邏輯更清晰簡潔;2.通過鍊式調用map實現嵌套取值,安全地避免NPE,任一環節為null則自動終止並返回默認值;3.filter可用於條件篩選,滿足條件才繼續執行後續操作,否則直接跳到orElse,適合輕量級業務判斷;4.不建議過度使用Optional,如基本類型或簡單邏輯中其反而增加複雜度,部分場景直接返回nu

遇到java.io.NotSerializableException的核心解決方法是確保所有需序列化的類實現Serializable接口,並檢查嵌套對象的序列化支持。 1.給主類添加implementsSerializable;2.確保類中自定義字段對應的類也實現Serializable;3.用transient標記不需要序列化的字段;4.檢查集合或嵌套對像中的非序列化類型;5.查看異常信息定位具體哪個類未實現接口;6.對無法修改的類考慮替換設計,如保存關鍵數據或使用可序列化的中間結構;7.考慮改

處理Java中的字符編碼問題,關鍵是在每一步都明確指定使用的編碼。 1.讀寫文本時始終指定編碼,使用InputStreamReader和OutputStreamWriter並傳入明確的字符集,避免依賴系統默認編碼。 2.在網絡邊界處理字符串時確保兩端一致,設置正確的Content-Type頭並用庫顯式指定編碼。 3.謹慎使用String.getBytes()和newString(byte[]),應始終手動指定StandardCharsets.UTF_8以避免平台差異導致的數據損壞。總之,通過在每個階段

JavaSocket編程是網絡通信的基礎,通過Socket實現客戶端與服務器間的數據交換。 1.Java中Socket分為客戶端使用的Socket類和服務器端使用的ServerSocket類;2.編寫Socket程序需先啟動服務器監聽端口,再由客戶端發起連接;3.通信過程包括連接建立、數據讀寫及流關閉;4.注意事項包括避免端口衝突、正確配置IP地址、合理關閉資源及支持多客戶端的方法。掌握這些即可實現基本的網絡通信功能。

在Java中,Comparable用於類內部定義默認排序規則,Comparator用於外部靈活定義多種排序邏輯。 1.Comparable是類自身實現的接口,通過重寫compareTo()方法定義自然順序,適用於類有固定、最常用的排序方式,如String或Integer。 2.Comparator是外部定義的函數式接口,通過compare()方法實現,適合同一類需要多種排序方式、無法修改類源碼或排序邏輯經常變化的情況。兩者區別在於Comparable只能定義一種排序邏輯且需修改類本身,而Compar

遍歷Java中的Map有三種常用方法:1.使用entrySet同時獲取鍵和值,適用於大多數場景;2.使用keySet或values分別遍歷鍵或值;3.使用Java8的forEach簡化代碼結構。 entrySet返回包含所有鍵值對的Set集合,每次循環獲取Map.Entry對象,適合頻繁訪問鍵和值的情況;若只需鍵或值,可分別調用keySet()或values(),也可在遍歷鍵時通過map.get(key)獲取值;Java8中可通過Lambda表達式使用forEach((key,value)->

InJava,thestatickeywordmeansamemberbelongstotheclassitself,nottoinstances.Staticvariablesaresharedacrossallinstancesandaccessedwithoutobjectcreation,usefulforglobaltrackingorconstants.Staticmethodsoperateattheclasslevel,cannotaccessnon-staticmembers,
