首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 你可能不知道的事 (3) - HashMap 核心原理深度解析

     編輯:Java 綜合教學

HashMap 內部機制全景圖:從存儲結構到擴容機制

HashMap 是 Java 開發者每天都在用的集合,但其底層實現細節往往被忽視。本文通過源碼級分析,揭示 HashMap 的存取流程、哈希衝突解決、擴容觸發條件,並解答「為什麼推薦用 String 做 Key」等高頻疑問。

核心結構:數組 + 鏈表(JDK 7)/ 紅黑樹(JDK 8)

HashMap 內部結構

  • transient Entry<K,V>[] table:核心存儲數組,長度始終為 2 的冪次。
  • Entry 節點包含 hash、key、value、next 指針,形成單向鏈表。
  • JDK 8 引入紅黑樹優化長鏈表查詢性能(鏈表長度 > 8 且數組長度 > 64 時轉換)。

存儲流程:put(K, V) 深度追蹤

  1. 初始化:無參構造器設置默認容量 16、負載因子 0.75,table 初為空數組 EMPTY_TABLE。
  2. 首次 put 觸發 inflateTable:將 table 擴容為 16。
  3. 計算索引:hash(key) → indexFor(hash, table.length)(位運算 h & (length-1))。
  4. 衝突處理:遍歷該索引位置鏈表,若 hash 相等且 key.equals() 則覆蓋值;否則頭插法新增節點。
  5. 擴容檢查:size >= threshold 且當前索引位置非空 → resize(2 * table.length),重新計算所有節點索引並遷移。

示例演示:依次 put "one"~"seven",索引分佈與鏈表形成過程圖解。

取值流程:get(K) 高效定位

  1. 計算 key 的 hash 與索引。
  2. 定位 table[index] 遍歷鏈表/紅黑樹。
  3. 比對 hash 相等且 key.equals() 即命中,返回 value。

null key 特殊處理:hash 固定為 0,存放於 table[0],getForNullKey 遍歷查找。

四大經典問題深度解答

Q1:兩個不同 Key hash 碼相同怎麼辦?

鏈表/紅黑樹節點儲存完整 key,遍歷時通過 equals() 精確比對,確保取到正確 Value。

Q2:相同 Key 重複 put 會怎樣?

put 過程中檢測到 key 相等(hash 相等且 equals 為 true),直接覆蓋舊值並返回舊值,保證 Key 唯一性。

Q3:為什麼推薦 String 作為 Key?

  • 不可變性:hashCode 計算後不會改變,避免「修改 Key 導致找不到 Value」的災難。
  • hashCode 緩存:String 內部緩存 hash 值(private int hash),多次調用極快。

Q4:可變對象能做 Key 嗎?

可以,但必須保證參與 hashCode 計算的字段不變。示例:Key 類基於 name 計算 hash,修改 address 不影響查找;若修改 name 則導致 get 返回 null。

性能調優關鍵參數

總結

理解 HashMap 的「數組+鏈表/紅黑樹」結構、「hash&(n-1)」索引算法、「頭插法/尾插法」擴容遷移差異(JDK 7/8),能幫助我們正確選擇 Key 類型、預估容量、排查並發問題(多線程下鏈表成環導致 CPU 100%)。日常開發中,優先使用 ConcurrentHashMap 解決線程安全需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
參數默認值建議
initialCapacity16預估數據量大時預設較大值,減少擴容次數
loadFactor0.75空間換時間;極少擴容場景可調大至 0.9