JAVA 集合框架Map 接口的深度解析與實(shí)戰(zhàn)指南
1.1 本章學(xué)習(xí)目標(biāo)與重點(diǎn)
?? 掌握 Map 接口的核心特性,理解 Key-Value 鍵值對的存儲結(jié)構(gòu)與設(shè)計(jì)思想。
?? 熟練掌握 HashMap、LinkedHashMap、TreeMap 等實(shí)現(xiàn)類的底層原理與適用場景。
?? 理解 Map 集合的線程安全問題,掌握并發(fā)環(huán)境下的解決方案。
?? 本章重點(diǎn)是 HashMap 的底層實(shí)現(xiàn)原理 和 不同 Map 實(shí)現(xiàn)類的性能對比,這是面試和開發(fā)中的高頻核心考點(diǎn)。
1.2 Map 接口核心概述
1.2.1 Map 接口的定義與特性
?? Map 是一種鍵值對(Key-Value) 集合,它的核心是通過鍵(Key)來唯一標(biāo)識值(Value)。
Map 接口中的 Key 具有唯一性,不能重復(fù);Value 可以重復(fù),并且可以為 null。
Map 接口與 Collection 接口是并列關(guān)系,它不屬于 Collection 體系,沒有繼承關(guān)系。
Map 接口的核心方法:
put(K key, V value):添加鍵值對,Key 重復(fù)時會覆蓋原有 Valueget(Object key):根據(jù) Key 獲取 Value,Key 不存在時返回nullremove(Object key):根據(jù) Key 刪除對應(yīng)的鍵值對containsKey(Object key):判斷是否包含指定 KeycontainsValue(Object value):判斷是否包含指定 ValuekeySet():獲取所有 Key 組成的 Set 集合values():獲取所有 Value 組成的 Collection 集合entrySet():獲取所有鍵值對(Map.Entry)組成的 Set 集合
? 核心結(jié)論:Map 適合通過唯一標(biāo)識(Key)快速查找對應(yīng)數(shù)據(jù)(Value)的場景。
1.2.2 Map 集合的遍歷方式
Map 集合有三種常用的遍歷方式,我們以 HashMap 為例進(jìn)行代碼實(shí)操:
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("name", "張三");
map.put("age", "20");
map.put("gender", "男");
// 方式1:遍歷所有Key,通過Key獲取Value
System.out.println("方式1:遍歷Key獲取Value");
Set<String> keySet = map.keySet();
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
// 方式2:遍歷所有鍵值對(Map.Entry)
System.out.println("\n方式2:遍歷Map.Entry");
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
// 方式3:JDK8+ Lambda表達(dá)式遍歷
System.out.println("\n方式3:Lambda表達(dá)式遍歷");
map.forEach((key, value) -> System.out.println(key + "=" + value));
}
}輸出結(jié)果
方式1:遍歷Key獲取Value
name=張三
age=20
gender=男方式2:遍歷Map.Entry
name=張三
age=20
gender=男方式3:Lambda表達(dá)式遍歷
name=張三
age=20
gender=男
? 核心結(jié)論:遍歷大量數(shù)據(jù)時,方式2(entrySet)效率最高,因?yàn)樗苊饬送ㄟ^ Key 重復(fù)查詢 Value 的操作。
1.3 HashMap:基于哈希表的實(shí)現(xiàn)
1.3.1 HashMap 底層原理(JDK8)
?? JDK8 中 HashMap 的底層結(jié)構(gòu)是 數(shù)組 + 鏈表 + 紅黑樹 的組合結(jié)構(gòu),目的是解決哈希沖突,提升查詢效率。
- 數(shù)組(哈希桶):數(shù)組的每個元素是一個鏈表或紅黑樹的頭節(jié)點(diǎn),默認(rèn)初始容量為 16,默認(rèn)加載因子為 0.75。
- 鏈表:當(dāng)多個 Key 的哈希值相同,且對應(yīng)數(shù)組下標(biāo)位置已有元素時,會以鏈表形式存儲,稱為哈希沖突。
- 紅黑樹:當(dāng)鏈表長度超過閾值(默認(rèn)為 8),并且數(shù)組長度大于等于 64 時,鏈表會轉(zhuǎn)換為紅黑樹,將查詢時間復(fù)雜度從
O(n)降低到O(log n)。
HashMap 的核心存儲流程:
① ?? 計(jì)算 Key 的哈希值:通過 hash(key) 方法計(jì)算,目的是降低哈希沖突概率。
② ?? 計(jì)算數(shù)組下標(biāo):(數(shù)組長度 - 1) & 哈希值,等價于取模運(yùn)算但效率更高。
③ ?? 判斷下標(biāo)位置是否為空:為空則直接插入新節(jié)點(diǎn);不為空則判斷 Key 是否重復(fù)。
④ ?? 處理 Key 重復(fù):Key 重復(fù)則覆蓋 Value;不重復(fù)則插入鏈表或紅黑樹。
⑤ ?? 擴(kuò)容判斷:當(dāng)元素個數(shù)超過 數(shù)組容量 * 加載因子 時,數(shù)組會擴(kuò)容為原來的 2 倍。
1.3.2 代碼實(shí)操:HashMap 的常用操作
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
// 1. 添加鍵值對
hashMap.put("語文", 90);
hashMap.put("數(shù)學(xué)", 95);
hashMap.put("英語", 92);
hashMap.put("數(shù)學(xué)", 100); // Key重復(fù),覆蓋原有Value
System.out.println("HashMap內(nèi)容:" + hashMap);
// 2. 根據(jù)Key獲取Value
Integer mathScore = hashMap.get("數(shù)學(xué)");
System.out.println("數(shù)學(xué)成績:" + mathScore);
// 3. 判斷是否包含指定Key或Value
boolean hasEnglish = hashMap.containsKey("英語");
boolean has90 = hashMap.containsValue(90);
System.out.println("包含英語Key:" + hasEnglish);
System.out.println("包含90分Value:" + has90);
// 4. 刪除鍵值對
hashMap.remove("語文");
System.out.println("刪除語文后的HashMap:" + hashMap);
// 5. 獲取集合大小
int size = hashMap.size();
System.out.println("HashMap大?。? + size);
// 6. 清空集合
hashMap.clear();
System.out.println("清空后是否為空:" + hashMap.isEmpty());
}
}輸出結(jié)果
HashMap內(nèi)容:{語文=90, 數(shù)學(xué)=100, 英語=92}
數(shù)學(xué)成績:100
包含英語Key:true
包含90分Value:true
刪除語文后的HashMap:{數(shù)學(xué)=100, 英語=92}
HashMap大?。?
清空后是否為空:true
1.3.3 自定義對象作為 Key 的注意事項(xiàng)
?? 當(dāng)使用自定義對象作為 HashMap 的 Key 時,必須重寫 hashCode() 和 equals() 方法,否則無法保證 Key 的唯一性。
我們以 Student 類為例,實(shí)現(xiàn)基于學(xué)號的 Key 唯一性:
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
class Student {
private String id;
private String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
// 重寫equals方法:根據(jù)學(xué)號判斷Key是否相同
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
// 重寫hashCode方法:根據(jù)學(xué)號計(jì)算哈希值
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Student{id='" + id + "', name='" + name + "'}";
}
}
public class HashMapCustomKeyDemo {
public static void main(String[] args) {
Map<Student, String> studentMap = new HashMap<>();
Student s1 = new Student("001", "張三");
Student s2 = new Student("002", "李四");
Student s3 = new Student("001", "張三"); // 與s1學(xué)號相同
studentMap.put(s1, "一班");
studentMap.put(s2, "二班");
studentMap.put(s3, "三班"); // Key重復(fù),覆蓋s1的Value
// 遍歷輸出
studentMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}輸出結(jié)果
Student{id='001', name='張三'}=三班
Student{id='002', name='李四'}=二班
1.3.4 HashMap 性能分析
- 增刪查操作:理想情況下時間復(fù)雜度為
O(1),哈希沖突嚴(yán)重時會退化為O(n),紅黑樹轉(zhuǎn)換后為O(log n)。 - 擴(kuò)容機(jī)制:擴(kuò)容時需要重新計(jì)算所有元素的下標(biāo),非常消耗性能,開發(fā)中建議提前指定初始容量,減少擴(kuò)容次數(shù)。
?? 注意事項(xiàng):HashMap 是線程不安全的集合,多線程環(huán)境下使用會出現(xiàn)數(shù)據(jù)錯亂或ConcurrentModificationException異常。
1.4 LinkedHashMap:有序的哈希表
1.4.1 LinkedHashMap 底層原理
?? LinkedHashMap 是 HashMap 的子類,底層結(jié)構(gòu)是 HashMap + 雙向鏈表。
它通過雙向鏈表維護(hù)鍵值對的插入順序或訪問順序,保證遍歷順序與插入順序一致,或者與最近訪問順序一致。
LinkedHashMap 的元素唯一性判斷規(guī)則與 HashMap 完全相同。
1.4.2 代碼實(shí)操1:插入順序模式(默認(rèn))
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapInsertOrderDemo {
public static void main(String[] args) {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("b", "B");
linkedHashMap.put("a", "A");
linkedHashMap.put("c", "C");
// 遍歷順序與插入順序一致
linkedHashMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}輸出結(jié)果
b=B
a=A
c=C
1.4.3 代碼實(shí)操2:訪問順序模式
通過構(gòu)造方法 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 可以開啟訪問順序模式,最近訪問的元素會被移到鏈表尾部。
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapAccessOrderDemo {
public static void main(String[] args) {
// 開啟訪問順序模式:accessOrder = true
Map<String, String> linkedHashMap = new LinkedHashMap<>(16, 0.75f, true);
linkedHashMap.put("a", "A");
linkedHashMap.put("b", "B");
linkedHashMap.put("c", "C");
System.out.println("初始順序:");
linkedHashMap.forEach((key, value) -> System.out.println(key + "=" + value));
// 訪問元素a,觸發(fā)訪問順序調(diào)整
linkedHashMap.get("a");
System.out.println("\n訪問元素a后的順序:");
linkedHashMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}輸出結(jié)果
初始順序:
a=A
b=B
c=C訪問元素a后的順序:
b=B
c=C
a=A
? 核心結(jié)論:訪問順序模式的 LinkedHashMap 可以用來實(shí)現(xiàn) LRU 緩存淘汰算法(最近最少使用淘汰)。
1.4.4 性能分析
- LinkedHashMap 的增刪查效率略低于 HashMap,因?yàn)樾枰S護(hù)雙向鏈表的節(jié)點(diǎn)引用。
- 適合需要有序遍歷且高效查找的場景,例如緩存系統(tǒng)、配置參數(shù)存儲等。
?? 注意事項(xiàng):LinkedHashMap 同樣是線程不安全的集合。
1.5 TreeMap:基于紅黑樹的排序映射
1.5.1 TreeMap 底層原理
?? TreeMap 的底層結(jié)構(gòu)是紅黑樹,它會自動對 Key 進(jìn)行排序,默認(rèn)是升序排列。
TreeMap 不允許 Key 為 null,因?yàn)榕判驎r會拋出空指針異常。
TreeMap 保證 Key 唯一性的方式是通過比較 Key 的大小,而不是 hashCode() 和 equals() 方法。
TreeMap 的兩種排序方式:
- 自然排序:Key 實(shí)現(xiàn)
Comparable接口,重寫compareTo()方法。 - 定制排序:創(chuàng)建 TreeMap 時傳入
Comparator比較器,自定義排序規(guī)則。
1.5.2 代碼實(shí)操1:自然排序
import java.util.Map;
import java.util.TreeMap;
public class TreeMapNaturalSortDemo {
public static void main(String[] args) {
Map<Integer, String> treeMap = new TreeMap<>();
treeMap.put(3, "C");
treeMap.put(1, "A");
treeMap.put(2, "B");
// 自動按Key升序排列
treeMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}輸出結(jié)果
1=A
2=B
3=C
1.5.3 代碼實(shí)操2:定制排序
我們對字符串 Key 進(jìn)行降序排列,通過 Comparator 實(shí)現(xiàn)定制排序:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapCustomSortDemo {
public static void main(String[] args) {
// 傳入比較器,實(shí)現(xiàn)Key降序排列
Map<String, Integer> treeMap = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1); // 降序排列
}
});
treeMap.put("Java", 10);
treeMap.put("Python", 8);
treeMap.put("Go", 9);
treeMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}輸出結(jié)果
Python=8
Java=10
Go=9
1.5.4 性能分析
- 增刪查操作:時間復(fù)雜度穩(wěn)定為
O(log n),效率低于 HashMap,但支持有序遍歷。 - 適合需要排序和范圍查詢的場景,例如排行榜、字典排序等。
?? 注意事項(xiàng):
- TreeMap 是線程不安全的集合。
- 存儲自定義對象作為 Key 時,必須指定排序規(guī)則,否則會拋出
ClassCastException。
1.6 Hashtable:線程安全的哈希表
1.6.1 Hashtable 核心特性
?? Hashtable 是 Map 接口的早期實(shí)現(xiàn)類,底層結(jié)構(gòu)是 數(shù)組 + 鏈表(JDK8 沒有紅黑樹優(yōu)化)。
它的所有方法都添加了 synchronized 關(guān)鍵字,是線程安全的集合。
Hashtable 不允許 Key 或 Value 為 null,默認(rèn)初始容量為 11,加載因子為 0.75。
1.6.2 性能分析
- Hashtable 的線程安全是通過方法加鎖實(shí)現(xiàn)的,鎖粒度大,并發(fā)性能低。
- 現(xiàn)代開發(fā)中,不推薦使用 Hashtable,優(yōu)先使用 ConcurrentHashMap 實(shí)現(xiàn)線程安全。
1.7 ConcurrentHashMap:并發(fā)安全的哈希表
1.7.1 ConcurrentHashMap 核心特性
?? ConcurrentHashMap 是 JUC 包下的線程安全集合,專門用于解決 HashMap 的并發(fā)問題。
JDK8 中 ConcurrentHashMap 的底層結(jié)構(gòu)是 數(shù)組 + 鏈表 + 紅黑樹,與 HashMap 類似。
它采用分段鎖或 CAS + synchronized 的方式實(shí)現(xiàn)線程安全,鎖粒度小,并發(fā)性能遠(yuǎn)高于 Hashtable。
ConcurrentHashMap 支持 Key 和 Value 為 null(與 Hashtable 不同)。
1.7.2 代碼實(shí)操:ConcurrentHashMap 的使用
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 多線程環(huán)境下安全操作
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
concurrentMap.put("thread1_" + i, i);
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
concurrentMap.put("thread2_" + i, i);
}
}).start();
// 等待線程執(zhí)行完成
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ConcurrentHashMap大?。? + concurrentMap.size());
}
}輸出結(jié)果
ConcurrentHashMap大?。?000
? 核心結(jié)論:多線程環(huán)境下優(yōu)先使用 ConcurrentHashMap,兼顧線程安全和并發(fā)性能。
1.8 實(shí)戰(zhàn)案例:基于 Map 實(shí)現(xiàn) LRU 緩存
1.8.1 需求分析
?? 實(shí)現(xiàn)一個 LRU(最近最少使用)緩存工具類,滿足以下需求:
- 緩存容量有限,超出容量時自動淘汰最近最少使用的元素。
- 支持緩存的添加、查詢、刪除操作。
- 保證操作的時間復(fù)雜度盡可能低。
1.8.2 實(shí)現(xiàn)思路
利用 LinkedHashMap 的訪問順序模式實(shí)現(xiàn) LRU 緩存,重寫 removeEldestEntry() 方法,自定義淘汰規(guī)則。
1.8.3 代碼實(shí)現(xiàn)
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 基于LinkedHashMap實(shí)現(xiàn)的LRU緩存
*/
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
// 緩存最大容量
private final int maxCapacity;
// 構(gòu)造方法:開啟訪問順序模式
public LRUCache(int maxCapacity) {
super(16, 0.75f, true);
this.maxCapacity = maxCapacity;
}
/**
* 重寫該方法,自定義淘汰規(guī)則
* @param eldest 最久未使用的元素
* @return true表示淘汰該元素,false表示不淘汰
*/
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 當(dāng)元素個數(shù)超過最大容量時,淘汰最久未使用的元素
return size() > maxCapacity;
}
// 測試方法
public static void main(String[] args) {
LRUCache<String, Integer> cache = new LRUCache<>(3);
// 添加緩存元素
cache.put("A", 1);
cache.put("B", 2);
cache.put("C", 3);
System.out.println("初始緩存:" + cache);
// 訪問元素A,調(diào)整訪問順序
cache.get("A");
System.out.println("訪問A后的緩存:" + cache);
// 添加元素D,超出容量,淘汰最久未使用的B
cache.put("D", 4);
System.out.println("添加D后的緩存:" + cache);
}
}輸出結(jié)果
初始緩存:{A=1, B=2, C=3}
訪問A后的緩存:{B=2, C=3, A=1}
添加D后的緩存:{C=3, A=1, D=4}
1.8.4 案例總結(jié)
? 這個 LRU 緩存工具類充分利用了 LinkedHashMap 的特性,代碼簡潔且性能高效。
通過重寫 removeEldestEntry() 方法,輕松實(shí)現(xiàn)了緩存淘汰規(guī)則,在實(shí)際開發(fā)中可直接用于本地緩存場景。
1.9 本章總結(jié)
- Map 是鍵值對集合,Key 唯一,Value 可重復(fù),常用實(shí)現(xiàn)類有 HashMap、LinkedHashMap、TreeMap。
- HashMap 底層是數(shù)組+鏈表+紅黑樹,查詢效率高,適合快速查找場景,線程不安全。
- LinkedHashMap 基于 HashMap+雙向鏈表,支持插入順序或訪問順序遍歷,可實(shí)現(xiàn) LRU 緩存。
- TreeMap 底層是紅黑樹,支持 Key 排序,適合有序遍歷和范圍查詢場景,線程不安全。
- 多線程環(huán)境下,優(yōu)先使用 ConcurrentHashMap 保證線程安全,避免使用 Hashtable。
- 自定義對象作為 HashMap Key 時,必須重寫
hashCode()和equals()方法。
到此這篇關(guān)于JAVA 集合框架Map 接口的深度解析與實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)java map接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java枚舉通過Code獲取相應(yīng)的Value值實(shí)現(xiàn)方式
本文介紹了枚舉定義、如何通過code獲取value的方法,并提供了一個完整的代碼示例,通過實(shí)際測試,證明了該方法的有效性,希望本文能夠?yàn)樽x者提供參考,并鼓勵大家支持腳本之家2026-03-03
Java調(diào)用ChatGPT API并實(shí)現(xiàn)流式接收方式(Server-Sent Events,SSE)
文章介紹如何在Java中通過OkHttp和SSE技術(shù)實(shí)現(xiàn)流式獲取ChatGPT響應(yīng),解決傳統(tǒng)HTTP阻塞問題,提升用戶體驗(yàn),需配置stream參數(shù),利用SseEmitter封裝后端推送,前端使用EventSourcePolyfill插件處理Token,同時注意資源管理和避免換行符干擾2025-08-08
java利用CompletionService保證任務(wù)先完成先獲取到執(zhí)行結(jié)果
這篇文章主要為大家詳細(xì)介紹了java如何利用CompletionService來保證任務(wù)先完成先獲取到執(zhí)行結(jié)果,文中的示例代碼講解詳細(xì),需要的可以參考下2023-08-08
Java8實(shí)現(xiàn)Stream流的合并的方法展示
本文介紹了Java8中Stream流的合并方法,包括concat()、flatMap()和reduce()三種方法。其中,concat()方法可以將兩個Stream流合并成一個,flatMap()方法可以將一個Stream流中的元素映射成多個Stream流并合并成一個,reduce()方法可以將Stream流中的元素逐個合并成一個結(jié)果2023-05-05
java同步器AQS架構(gòu)AbstractQueuedSynchronizer原理解析
這篇文章主要為大家介紹了java同步器AQS架構(gòu)AbstractQueuedSynchronizer的底層原理及源碼解析,有需要的朋友可以借鑒參考下,希望能有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03
如何解決executors線程池創(chuàng)建的線程不釋放的問題
這篇文章主要介紹了如何解決executors線程池創(chuàng)建的線程不釋放的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
SpringBoot3集成SpringSecurity+JWT的實(shí)現(xiàn)
本文詳解SpringBoot3整合SpringSecurity與JWT實(shí)現(xiàn)認(rèn)證授權(quán),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-07-07

