數據結構-HashMap

在 Java 鍵值對(Key-Value)集合中,HashMap 是使用頻率最高的實現類之一,憑借高效的查找、插入性能,成為日常開發的 “利器”。本文將從 HashMap 的底層原理、核心特點、常用方法到遍歷方式、使用注意事項,進行系統性梳理,幫助快速掌握其核心邏輯與實戰技巧。

一、HashMap 核心認知:底層原理與特點

HashMap 本質是哈希表(數組 + 鏈表 / 紅黑樹)?實現的鍵值對存儲結構,核心目標是通過 “哈希算法” 快速定位元素,平衡查詢與增刪效率。其核心特點如下:

1. 底層存儲結構(JDK 1.8+)

  • 基礎結構:數組(稱為 “哈希桶”)+ 鏈表 + 紅黑樹。
    • 數組:每個元素是一個 “鏈表頭節點”,通過?key?的哈希值計算數組索引(index = (數組長度 - 1) & 哈希值),實現快速定位。
    • 鏈表:當多個?key?計算出相同索引(哈希沖突)時,元素以鏈表形式存儲在對應桶中。
    • 紅黑樹:當鏈表長度超過?8?且數組長度 ≥ 64 時,鏈表會轉為紅黑樹,將查詢時間復雜度從 O (n) 優化為 O (log n)(避免鏈表過長導致性能下降)。
  • 示意圖簡化理解
數組索引 0:[Node(key1, val1)] → [Node(key2, val2)]  // 鏈表(長度<8)
數組索引 1:[TreeNode(key3, val3)] → 紅黑樹結構       // 紅黑樹(長度≥8)
數組索引 2:null
...

2. 核心特點

  1. 鍵值對規則
    • Key 唯一:若添加重復 Key,新 Value 會覆蓋舊 Value(put()?方法返回舊 Value)。
    • Value 可重復:不同 Key 可對應相同 Value。
    • 允許 null:Key 最多允許 1 個 null(重復添加 null Key 會覆蓋),Value 可多個 null。
  2. 無序性:存儲順序與插入順序無關(底層按哈希值排序,非插入順序)。
  3. 線程不安全:非同步設計,多線程同時讀寫可能出現數據異常(如?ConcurrentModificationException),需手動處理線程安全(如?Collections.synchronizedMap()?或?ConcurrentHashMap)。
  4. 自動擴容
    • 默認初始容量:16(數組長度,必須是 2 的冪,確保哈希計算均勻)。
    • 負載因子:默認 0.75(當元素數量 ≥ 容量 × 負載因子時觸發擴容)。
    • 擴容規則:新容量 = 舊容量 × 2,同時重新計算所有元素的哈希索引(“rehash”),會消耗一定性能。
  5. 高效性能
    • 理想情況下,插入、查詢、刪除的時間復雜度均為?O(1)(直接通過哈希值定位桶)。
    • 哈希沖突較少時,性能接近理想值;沖突嚴重(鏈表過長)時,性能會下降(紅黑樹優化可緩解此問題)。

二、HashMap 常用方法

HashMap 提供了豐富的方法操作鍵值對,以下是開發中最常用的方法,均附完整示例代碼,可直接復制運行。

1. 基礎操作:添加、獲取、刪除

(1)添加鍵值對(put ())
  • V put(K key, V value):添加鍵值對,若 Key 已存在則覆蓋 Value,返回舊 Value(若 Key 不存在則返回 null)。
  • void putAll(Map<? extends K, ? extends V> m):將另一個同類型 Map 的所有鍵值對添加到當前 HashMap 中(重復 Key 會被覆蓋)。
import java.util.HashMap;public class HashMapPutDemo {public static void main(String[] args) {// 1. 單個鍵值對添加HashMap<String, Integer> scoreMap = new HashMap<>();Integer oldMathScore = scoreMap.put("數學", 90); // Key 不存在,返回 nullSystem.out.println("舊數學成績:" + oldMathScore); // 輸出:nullscoreMap.put("語文", 85);scoreMap.put("英語", 95);System.out.println("添加后:" + scoreMap); // 輸出:{數學=90, 語文=85, 英語=95}// 重復 Key 覆蓋:數學成績從 90 改為 98Integer updatedOldScore = scoreMap.put("數學", 98);System.out.println("被覆蓋的舊數學成績:" + updatedOldScore); // 輸出:90System.out.println("覆蓋后:" + scoreMap); // 輸出:{數學=98, 語文=85, 英語=95}// 2. 批量添加(putAll())HashMap<String, Integer> extraScoreMap = new HashMap<>();extraScoreMap.put("物理", 88);extraScoreMap.put("化學", 92);scoreMap.putAll(extraScoreMap);System.out.println("批量添加后:" + scoreMap); // 輸出:{數學=98, 語文=85, 英語=95, 物理=88, 化學=92}}
}
(2)獲取值與判斷存在(get ()、containsKey ()、containsValue ())
  • V get(Object key):根據 Key 獲取 Value,若 Key 不存在則返回?null(注意:若 Value 本身是 null,需用?containsKey()?區分 “Key 不存在” 和 “Value 為 null”)。
  • boolean containsKey(Object key):判斷 HashMap 是否包含指定 Key,返回布爾值。
  • boolean containsValue(Object value):判斷 HashMap 是否包含指定 Value,返回布爾值。
public class HashMapGetContainsDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("語文", 85);scoreMap.put("生物", null); // Value 為 null// 1. 根據 Key 獲取 ValueInteger mathScore = scoreMap.get("數學");Integer historyScore = scoreMap.get("歷史"); // Key 不存在Integer bioScore = scoreMap.get("生物"); // Value 本身是 nullSystem.out.println("數學成績:" + mathScore); // 輸出:98System.out.println("歷史成績(Key 不存在):" + historyScore); // 輸出:nullSystem.out.println("生物成績(Value 為 null):" + bioScore); // 輸出:null// 2. 判斷 Key 是否存在(區分“Key 不存在”和“Value 為 null”)boolean hasBioKey = scoreMap.containsKey("生物");boolean hasHistoryKey = scoreMap.containsKey("歷史");System.out.println("是否包含 Key '生物':" + hasBioKey); // 輸出:trueSystem.out.println("是否包含 Key '歷史':" + hasHistoryKey); // 輸出:false// 3. 判斷 Value 是否存在boolean has98 = scoreMap.containsValue(98);boolean has100 = scoreMap.containsValue(100);System.out.println("是否包含 Value 98:" + has98); // 輸出:trueSystem.out.println("是否包含 Value 100:" + has100); // 輸出:false}
}
(3)刪除鍵值對(remove ())
  • V remove(Object key):根據 Key 刪除鍵值對,返回被刪除的 Value(若 Key 不存在則返回 null)。
public class HashMapRemoveDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("語文", 85);scoreMap.put("英語", 95);// 刪除 Key 為“語文”的鍵值對Integer removedChineseScore = scoreMap.remove("語文");System.out.println("被刪除的語文成績:" + removedChineseScore); // 輸出:85System.out.println("刪除后:" + scoreMap); // 輸出:{數學=98, 英語=95}// 刪除不存在的 KeyInteger removedHistoryScore = scoreMap.remove("歷史");System.out.println("刪除不存在的 Key 返回值:" + removedHistoryScore); // 輸出:null}
}

2. 進階操作:修改、清空、判斷空否

(1)修改 Value(replace ())
  • V replace(K key, V value):僅當 Key 存在時,用新 Value 替換舊 Value,返回舊 Value(若 Key 不存在則返回 null,區別于?put()put()?會新增不存在的 Key)。
public class HashMapReplaceDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("英語", 95);// 修改存在的 Key(英語成績從 95 改為 97)Integer oldEnglishScore = scoreMap.replace("英語", 97);System.out.println("被修改的舊英語成績:" + oldEnglishScore); // 輸出:95System.out.println("修改后:" + scoreMap); // 輸出:{數學=98, 英語=97}// 修改不存在的 Key(不會新增,返回 null)Integer oldHistoryScore = scoreMap.replace("歷史", 80);System.out.println("修改不存在的 Key 返回值:" + oldHistoryScore); // 輸出:nullSystem.out.println("修改后集合:" + scoreMap); // 輸出:{數學=98, 英語=97}(無變化)}
}
(2)清空與判斷空否(clear ()、isEmpty ()、size ())
  • void clear():清空 HashMap 中所有鍵值對(集合變為空,對象本身仍存在)。
  • boolean isEmpty():判斷 HashMap 是否為空(元素個數為 0),返回布爾值。
  • int size():返回 HashMap 中鍵值對的實際個數(區別于 “容量”)。
public class HashMapClearEmptySizeDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("英語", 97);// 1. 獲取集合大小System.out.println("初始元素個數:" + scoreMap.size()); // 輸出:2// 2. 判斷是否為空System.out.println("初始是否為空:" + scoreMap.isEmpty()); // 輸出:false// 3. 清空集合scoreMap.clear();System.out.println("清空后元素個數:" + scoreMap.size()); // 輸出:0System.out.println("清空后是否為空:" + scoreMap.isEmpty()); // 輸出:true}
}

3. HashMap 三種核心遍歷方式

HashMap 存儲的是 “鍵值對(Entry)”,遍歷需圍繞 “Key 集合”“Value 集合”“Entry 集合” 展開,三種常用方式如下:

(1)遍歷 Key 集合,再獲取 Value(keySet ())

通過?keySet()?獲取所有 Key 的集合,遍歷 Key 后用?get(key)?獲取對應 Value,適合僅需 Key 或需通過 Key 處理 Value 的場景。

public class HashMapKeySetDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("語文", 85);scoreMap.put("英語", 97);// 遍歷 Key 集合for (String subject : scoreMap.keySet()) {Integer score = scoreMap.get(subject);System.out.println(subject + ":" + score);}// 輸出(順序不固定):// 數學:98// 語文:85// 英語:97}
}
(2)直接遍歷 Entry 集合(entrySet ())

通過?entrySet()?獲取所有鍵值對(Map.Entry<K, V>)的集合,直接獲取 Key 和 Value,效率最高(無需二次?get(key)?查詢),是開發首選。

public class HashMapEntrySetDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("語文", 85);scoreMap.put("英語", 97);// 遍歷 Entry 集合(推薦)for (HashMap.Entry<String, Integer> entry : scoreMap.entrySet()) {String subject = entry.getKey(); // 獲取 KeyInteger score = entry.getValue(); // 獲取 ValueSystem.out.println(subject + ":" + score);}// 輸出(順序不固定):// 數學:98// 語文:85// 英語:97}
}
(3)遍歷 Value 集合(values ())

通過?values()?獲取所有 Value 的集合,僅遍歷 Value,適合無需 Key、僅需處理 Value 的場景(無法通過 Value 反向獲取 Key)。

public class HashMapValuesDemo {public static void main(String[] args) {HashMap<String, Integer> scoreMap = new HashMap<>();scoreMap.put("數學", 98);scoreMap.put("語文", 85);scoreMap.put("英語", 97);// 遍歷 Value 集合System.out.println("所有成績:");for (Integer score : scoreMap.values()) {System.out.println(score);}// 輸出(順序不固定):// 98// 85// 97}
}

三、HashMap 使用注意事項

  1. Key 的選擇原則

    • Key 必須重寫?hashCode()?和?equals()?方法(否則無法正確判斷 Key 唯一性,導致哈希沖突無法解決)。
    • 推薦使用不可變類作為 Key(如?StringInteger):若 Key 是可變對象,修改后哈希值變化,會導致無法通過原 Key 獲取 Value。
    • 示例:若用?User?類作為 Key,需手動重寫方法:
class User {private String id;// 重寫 hashCode() 和 equals()@Overridepublic int hashCode() { return id.hashCode(); }@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return Objects.equals(id, user.id);}
}
  1. null 鍵值的注意事項

    • Key 最多 1 個 null,重復添加會覆蓋;Value 可多個 null。
    • 用?get(key)?獲取 Value 時,若返回 null,需通過?containsKey(key)?確認是 “Key 不存在” 還是 “Value 為 null”。
  2. 線程安全問題

    • 單線程環境:直接使用 HashMap 即可。
    • 多線程環境:
      • 若需弱一致性:使用?Collections.synchronizedMap(new HashMap<>())(對整個 HashMap 加鎖,性能較低)。
      • 若需高性能:優先使用?ConcurrentHashMap(JDK 1.8+ 采用分段鎖,性能優于同步 HashMap)。
  3. 性能優化技巧

    • 初始容量指定:若已知元素數量,創建時指定初始容量(如?new HashMap<>(100)),避免頻繁擴容(擴容需 rehash,消耗性能)。
    • 負載因子調整:默認 0.75 是 “性能與空間” 的平衡,若內存充足可降低(如 0.5,減少哈希沖突),若內存緊張可提高(如 0.8,減少數組占用空間)。
    • 避免哈希沖突:合理重寫 Key 的?hashCode()?方法,盡量讓哈希值均勻分布,減少鏈表 / 紅黑樹的長度。

與 TreeMap/Hashtable 的區別(避免混淆)

特性HashMapTreeMapHashtable(不推薦)
排序無序(按哈希值)有序(Key 自然排序 / 自定義排序)無序
線程安全非線程安全非線程安全線程安全(全方法同步)
null 允許Key 1 個 null,Value 多個 null不允許 null不允許 null
底層結構數組 + 鏈表 / 紅黑樹紅黑樹數組 + 鏈表
適用場景通用高效查詢需要有序鍵值對遺留多線程場景(已被 ConcurrentHashMap 替代)

四、總結

HashMap 是 Java 鍵值對集合的核心實現,核心優勢在于 “哈希表” 帶來的 O (1) 高效性能,適合大多數無需有序、單線程的鍵值對存儲場景。掌握其底層結構(數組 + 鏈表 / 紅黑樹)、常用方法(put/get/remove/ 遍歷)及使用注意事項(Key 重寫方法、線程安全、性能優化),就能在開發中靈活應對各類場景。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/94290.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/94290.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/94290.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[系統架構設計師]安全架構設計理論與實踐(十八)

[系統架構設計師]安全架構設計理論與實踐&#xff08;十八&#xff09; 一.信息安全面臨的威脅 1.信息系統安全威脅的來源 物理環境&#xff0c;通信鏈路&#xff0c;網絡系統&#xff0c;操作系統&#xff0c;應用系統&#xff0c;管理系統 2.網絡與信息安全風險類別 風險類別…

AI適老服務暖人心:AI適老機頂盒破數字鴻溝、毫米波雷達護獨居安全,銀發生活新保障

銀發經濟領域長期受限于 “專業照護資源稀缺”“老年人數字適應能力弱”“獨居老人安全隱患多” 的困境&#xff0c;而 AI 技術的適老化改造&#xff0c;正讓銀發服務從 “被動保障” 轉向 “主動關懷”&#xff0c;既能幫老年人跨越數字鴻溝&#xff0c;又能為獨居老人筑起安全…

Linux應用軟件編程---網絡編程1(目的、網絡協議、網絡配置、UDP編程流程)

Linux下的網絡編程一、目的不同主機&#xff0c;進程間通信。二、解決的問題1. 主機與主機之間物理層面必須互聯互通。 2. 進程與進程在軟件層面必須互聯互通。物理層面的互聯互通流程圖如下&#xff1a;其中&#xff1a;IP地址&#xff1a;計算機的軟件地址&#xff0c;用來標…

常見開源協議詳解:哪些行為被允許?哪些被限制?

常見開源協議詳解&#xff1a;哪些行為被允許&#xff1f;哪些被限制&#xff1f; 開源世界的魅力在于共享與合作&#xff0c;但不同的開源協議對分發、修改、再發布以及宣傳/推廣有不同的要求和限制。很多開發者在 fork 項目、改 README、放到自己倉庫并在自媒體傳播 時&…

服務器硬盤進行分區和掛載

查看服務器上的硬盤&#xff1a;lsblk -d -o NAME,SIZE,MODEL可以看到我的硬盤是除了vda系統盤以外&#xff0c;還有個vdb。我們查看一下分區&#xff1a;lsblk可以看到&#xff1a;vdb 1T disk &#xff08;底下沒有分區&#xff0c;也沒有掛載&#xff09;我們想要用起來這…

【C初階】數據在內存中的存儲

目錄 1. 整數在內存中的存儲 2. 大小端字節序 2.1 什么是大小端&#xff1f; 2.2 為什么有大小端&#xff1f; 2.3 練習 2.3.1 練習1 2.3.2 練習2 2.3.3 練習3 2.3.4 練習4 2.3.5 練習5 2.3.6 練習6 3. 浮點數在內存中的存儲 3.1 浮點數存儲的過程 3.2 浮點數的取…

AI 自動化編程 trae 體驗2 幫我分析一個項目

總結&#xff1a; 接手一個項目可以讓trae 幫忙分析 上次講到trae在處理組件引入的時候&#xff0c;經常會碰到版本問題&#xff0c;分析引入了互聯網上非本版本或者有bug的代碼。主要依賴互聯網的資源庫。 但是分析一個項目應該是沒問題。 這次表現非常好&#xff0c;接手一個…

VMware虛擬機中CentOS 7 報錯 ping: www.xxx.com: Name or service not known

1:主要原因是網絡配置的問題 2:其實就是下面三張圖片中的,物理機虛擬網卡 vmware8 和虛擬機網絡編輯器&#xff0c;如果設置靜態IP 就是這三個地方的問題最簡單的解決辦法第一步&#xff1a;還原虛擬機網絡點擊確認后 ** 第二步給自己的虛擬機設置網絡連接方式 選擇NAT模式連接…

Java面試-自動裝箱與拆箱機制解析

&#x1f44b; 歡迎閱讀《Java面試200問》系列博客&#xff01; &#x1f680;大家好&#xff0c;我是Jinkxs&#xff0c;一名熱愛Java、深耕技術一線的開發者。在準備和參與了數十場Java面試后&#xff0c;我深知面試不僅是對知識的考察&#xff0c;更是對理解深度與表達能力的…

《VMware 安裝 CentOS 7.9 虛擬機詳細教程(含圖解步驟)》

目錄1.安裝前準備1.1 準備VMware軟件1.1.1 方式一1.1.2 方式二1.2 準備centos7.9鏡像1.2.1 方式一1.2.2 方式二2.安裝centos7.91.安裝前準備 1.1 準備VMware軟件 VMware需要的激活碼百度直接搜索vmware workstation17激活碼就可以搜索到 1.1.1 方式一 這種方式需要注冊官網的…

新能源知識庫(84)什么是IEC白皮書

IEC白皮書是由國際電工委員會&#xff08;IEC&#xff09;發布的戰略性技術文件&#xff0c;旨在針對新興技術和社會發展趨勢&#xff0c;提出標準化需求和發展路徑&#xff0c;為全球產業提供前瞻性指導。在新能源領域&#xff0c;IEC白皮書是推動技術創新、產業協同和國際規則…

從零開始學習JavaWeb-15

??一、數據庫安全與防注入實戰??1. ??SQL 注入原理與危害????攻擊本質??&#xff1a;利用輸入漏洞篡改 SQL 語義&#xff0c;例如&#xff1a;SELECT * FROM users WHERE username admin OR 11 -- AND password xxxOR 11導致條件永真&#xff0c;繞過密碼驗證。?…

深入理解深度學習中的“Batch”

文章目錄 **一、什么是Batch?為什么需要它?** **二、Batch Size(批次大小)的影響** **三、Batch, Epoch 和 Iteration 的關系** **四、案例分析** 在深度學習領域,“Batch”(批次)是一個核心且至關重要的概念。它指的是在模型訓練過程中,一次性輸入給神經網絡進行處理的…

27.語言模型

語言模型&#xff0c;是NLP方向一直主力研究的&#xff0c;通過訓練機器&#xff0c;來讓機器學習人類語言的內在規律&#xff0c;理解自然語言&#xff0c;并將其轉換為計算機語言。 目前的主流語言模型&#xff0c;如GPT、Deepseek等&#xff0c;并不是簡單的搜索背誦。他們的…

小智ai+mcp+n8n的智能組合

小智aimcpn8n的智能組合1 小智ai的版本2 n8n的配置3 mcp的demo4 工作流json? 之前有寫過小智ai的介紹&#xff0c;它提供了流暢且豐富的用戶語音交互能力。n8n提供了靈活且穩定的后臺工作流的能力&#xff0c;如果這兩個工具進行組合&#xff0c;可以打造一個好玩又好用的智能…

【DataGrip】連接達夢數據庫后,能查詢數據但是看不到表的幾種情況分析,達夢數據庫驅動包下載DmJdbcDriver18.jar

大概分為以下兩類情況&#xff0c;配置問題和驅動包的問題 DmJdbcDriver18.jar點擊下載 1.配置了表不可見 左上角點擊過濾的圖標&#xff0c;把table勾上就可以 2.Introspect using JDBC metadata 未勾選 1&#xff09;老版本的DataGrip 在options選項下 3&#xff09;新版…

全面解析 `strncasecmp` 字符串比較函數

1) 函數的概念與用途 strncasecmp 是 C 語言中一個非常實用的字符串處理函數&#xff0c;它執行不區分大小寫的字符串比較&#xff0c;但只比較前 n 個字符。這個函數的名字來源于"string n case-compare"&#xff08;字符串前n個字符不區分大小寫比較&#xff09;。…

高級SQL優化 | 告別 Hive 中 GROUP BY 的大 KEY 數據傾斜!PawSQL 自適應優化算法詳解

數據傾斜讓你的Hive查詢慢如蝸牛&#xff1f;單個熱點分組拖垮整個集群&#xff1f;PawSQL獨家算法GroupSkewedOptimization來拯救&#xff01;&#x1f3af; 痛點直擊&#xff1a;當數據傾斜遇上分組操作想象這樣一個場景&#xff1a;你的電商平臺有1000萬VIP用戶訂單和100萬普…

HUMS 2023齒輪箱數據分析

HUMS問答&#xff1a;https://humsconference.com.au/HUMS2023datachallenge/questions-answers.html 數據集申請&#xff1a;https://www.dst.defence.gov.au/our-technologies/helicopter-main-rotor-gearbox-planet-gear-fatigue-crack-propagation-test 歷年試卷&#xff1…

智慧工地:科技賦能與管理革新下的建筑業新圖景

隨著數字技術的深度滲透&#xff0c;智慧工地正以“技術落地 行業變革 管理創新”的三重突破&#xff0c;重構施工場景的核心邏輯&#xff0c;推動建筑業從傳統粗放式發展向精細化、智能化轉型。一、技術落地&#xff1a;用科技筑牢安全防線&#xff0c;提升施工效率技術是智…