Java中的HashMap實現(xiàn)原理深入理解
一、前言
在 Java 開發(fā)中,HashMap 是我們最常用的集合類之一。無論是緩存、配置存儲,還是數(shù)據(jù)傳輸,HashMap 都扮演著重要角色。但你是否真正了解它的底層實現(xiàn)?為什么它查找這么快?什么時候會退化成鏈表?JDK1.8 之后又有哪些優(yōu)化?
本文將帶你深入理解 HashMap 的實現(xiàn)原理,幫助你從“會用”到“懂原理”。
二、HashMap 的基本結(jié)構(gòu)
1. 底層數(shù)據(jù)結(jié)構(gòu)(JDK 1.8 之前 vs 之后)
| 版本 | 數(shù)據(jù)結(jié)構(gòu) |
|---|---|
| JDK 1.7 | 數(shù)組 + 鏈表 |
| JDK 1.8 | 數(shù)組 + 鏈表 + 紅黑樹 |
解釋:
數(shù)組:
HashMap的主干是一個Node<K,V>[] table,每個元素是一個桶(bucket)。鏈表:當(dāng)發(fā)生哈希沖突時,多個鍵值對會以鏈表形式存儲在同一個桶中。
紅黑樹:當(dāng)鏈表長度超過 8 且數(shù)組長度大于 64 時,鏈表會轉(zhuǎn)為紅黑樹,提升查詢效率。
三、核心源碼解析
1. 構(gòu)造函數(shù)與初始容量
public HashMap(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}initialCapacity:初始容量,必須是 2 的冪。loadFactor:負(fù)載因子,默認(rèn)是0.75,用于控制擴(kuò)容時機(jī)。
2. put 方法流程
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}步驟如下:
計算 key 的 hash 值(
hash(key))定位桶位置(
(n - 1) & hash)如果桶為空,直接插入
如果桶不為空,遍歷鏈表或紅黑樹
如果 key 已存在,覆蓋 value
如果插入后長度超過閾值,觸發(fā)擴(kuò)容或樹化
3. 哈希函數(shù)設(shè)計
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}目的: 減少哈希沖突,讓高位也參與運算,提升分布均勻性。
四、擴(kuò)容機(jī)制(resize)
當(dāng)元素個數(shù)超過 threshold = capacity * loadFactor 時,會觸發(fā)擴(kuò)容:
容量翻倍(
newCap = oldCap << 1)重新計算每個元素的位置(要么在原位置,要么在原位置 + oldCap)
優(yōu)化點: JDK 1.8 中不需要重新計算 hash,只需看新增的那一位是 0 還是 1。
五、線程安全問題
??
HashMap是線程不安全的!
問題表現(xiàn):
多線程 put 可能導(dǎo)致鏈表成環(huán)(JDK 1.7)
數(shù)據(jù)丟失、覆蓋等問題
解決方案:
| 方式 | 說明 |
|---|---|
Collections.synchronizedMap() | 包裝器,性能差 |
ConcurrentHashMap | 推薦,分段鎖/CAS 實現(xiàn),線程安全且高效 |
六、面試高頻問題總結(jié)
| 問題 | 簡答 |
|---|---|
| HashMap 的底層結(jié)構(gòu)? | 數(shù)組 + 鏈表 + 紅黑樹(JDK 1.8) |
| 為什么容量必須是 2 的冪? | 位運算效率高,hash & (n-1) 替代取模 |
| 什么時候轉(zhuǎn)紅黑樹? | 鏈表長度 > 8 且數(shù)組長度 > 64 |
| 為什么加載因子是 0.75? | 平衡空間與時間效率 |
| 如何線程安全地使用 Map? | 使用 ConcurrentHashMap |
七、總結(jié)思維導(dǎo)圖(文字版)
HashMap ├── 結(jié)構(gòu):數(shù)組 + 鏈表 + 紅黑樹 ├── 核心方法:put、get、resize、hash ├── 優(yōu)化:樹化、擴(kuò)容、hash 散列 ├── 線程安全:ConcurrentHashMap └── 面試點:容量、負(fù)載因子、沖突處理、樹化條件
八、附錄:手寫一個簡易 HashMap(練習(xí))
public class MyHashMap<K, V> {
private Node<K, V>[] table;
private int size;
static class Node<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
public void put(K key, V value) {
// 簡化版,省略擴(kuò)容、樹化等邏輯
}
public V get(K key) {
// 簡化版
return null;
}
}九、結(jié)語
理解 HashMap 的底層實現(xiàn),不僅能幫助你在面試中脫穎而出,更能在實際開發(fā)中避免踩坑。希望本文能為你打下堅實的基礎(chǔ)。
如果你覺得這篇文章對你有幫助,歡迎點贊、收藏、評論!
后續(xù)我還會更新《ConcurrentHashMap 源碼解析》《Java 集合框架全景圖》等內(nèi)容,記得關(guān)注我哦!
十、參考資料
《Java 編程思想》
《Java 并發(fā)編程實戰(zhàn)》
到此這篇關(guān)于Java中的HashMap實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Java HashMap實現(xiàn)原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis-plus的selectPage()分頁查詢不生效問題解決
本文主要介紹了Mybatis-plus的selectPage()分頁查詢不生效問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
java selenium Selenium IDE介紹及用法
本文主要介紹java selenium Selenium IDE,這里整理了相關(guān)資料和介紹如何安裝 Selenium IDE和使用方法,有需要的小伙伴可以參考下2016-08-08
關(guān)于SpringBoot獲取IOC容器中注入的Bean(推薦)
本文通過實例代碼給大家詳解了springboot獲取ioc容器中注入的bean問題,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧2018-05-05
MyBatis-Plus多表聯(lián)查(動態(tài)查詢)的項目實踐
本文主要介紹了MyBatis-Plus多表聯(lián)查(動態(tài)查詢)的項目實踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08

