跳到主要内容Java 集合框架进阶:Map 接口核心原理与实战 | 极客日志Javajava算法
Java 集合框架进阶:Map 接口核心原理与实战
Java Map 接口是键值对集合,Key 唯一 Value 可重复。HashMap 基于哈希表,JDK8 采用数组加链表红黑树结构,解决哈希冲突并优化查询效率。LinkedHashMap 维护插入或访问顺序,常用于实现 LRU 缓存。TreeMap 基于红黑树自动排序 Key。Hashtable 线程安全但性能低,ConcurrentHashMap 通过 CAS 和分段锁实现高并发安全。自定义对象作 Key 需重写 hashCode 和 equals 方法。
黑客7 浏览 Java 集合框架进阶:Map 接口核心原理与实战

1.1 本章学习目标与重点
💡 掌握 Map 接口的核心特性,理解 Key-Value 键值对的存储结构与设计思想。
💡 熟练掌握 HashMap、LinkedHashMap、TreeMap 等实现类的底层原理与适用场景。
💡 理解 Map 集合的线程安全问题,掌握并发环境下的解决方案。
⚠️ 本章重点是 HashMap 的底层实现原理 和 不同 Map 实现类的性能对比,这是面试和开发中的高频核心考点。
1.2 Map 接口核心概述
1.2.1 Map 接口的定义与特性
💡 Map 是一种键值对(Key-Value) 集合,它的核心是通过键(Key)来唯一标识值(Value)。
Map 接口中的 Key 具有唯一性,不能重复;Value 可以重复,并且可以为 null。
Map 接口与 Collection 接口是并列关系,它不属于 Collection 体系,没有继承关系。
Map 接口的核心方法:
put(K key, V value):添加键值对,Key 重复时会覆盖原有 Value
get(Object key):根据 Key 获取 Value,Key 不存在时返回 null
remove(Object key):根据 Key 删除对应的键值对
containsKey(Object key):判断是否包含指定 Key
containsValue(Object value):判断是否包含指定 Value
keySet():获取所有 Key 组成的 Set 集合
values():获取所有 Value 组成的 Collection 集合
entrySet():获取所有键值对(Map.Entry)组成的 Set 集合
✅ 核心结论:Map 适合通过唯一标识(Key)快速查找对应数据(Value)的场景。
1.2.2 Map 集合的遍历方式
Map 集合有三种常用的遍历方式,我们以 HashMap 为例进行代码实操:
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(, );
map.put(, );
System.out.println();
Set<String> keySet = map.keySet();
(String key : keySet) {
map.get(key);
System.out.println(key + + value);
}
System.out.println();
Set<Map.Entry<String, String>> entrySet = map.entrySet();
(Map.Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + + entry.getValue());
}
System.out.println();
map.forEach((key, value) -> System.out.println(key + + value));
}
}
"张三"
"age"
"20"
"gender"
"男"
"方式 1:遍历 Key 获取 Value"
for
String
value
=
"="
"\n方式 2:遍历 Map.Entry"
for
"="
"\n方式 3:Lambda 表达式遍历"
"="
方式 1:遍历 Key 获取 Value name=张三 age=20 gender=男
方式 2:遍历 Map.Entry name=张三 age=20 gender=男
方式 3:Lambda 表达式遍历 name=张三 age=20 gender=男
✅ 核心结论:遍历大量数据时,方式 2(entrySet)效率最高,因为它避免了通过 Key 重复查询 Value 的操作。
1.3 HashMap:基于哈希表的实现
1.3.1 HashMap 底层原理(JDK8)
💡 JDK8 中 HashMap 的底层结构是 数组 + 链表 + 红黑树 的组合结构,目的是解决哈希冲突,提升查询效率。
- 数组(哈希桶):数组的每个元素是一个链表或红黑树的头节点,默认初始容量为 16,默认加载因子为 0.75。
- 链表:当多个 Key 的哈希值相同,且对应数组下标位置已有元素时,会以链表形式存储,称为哈希冲突。
- 红黑树:当链表长度超过阈值(默认为 8),并且数组长度大于等于 64 时,链表会转换为红黑树,将查询时间复杂度从
O(n) 降低到 O(log n)。
HashMap 的核心存储流程:
① 📝 计算 Key 的哈希值:通过 hash(key) 方法计算,目的是降低哈希冲突概率。
② 📝 计算数组下标:(数组长度 - 1) & 哈希值,等价于取模运算但效率更高。
③ 📝 判断下标位置是否为空:为空则直接插入新节点;不为空则判断 Key 是否重复。
④ 📝 处理 Key 重复:Key 重复则覆盖 Value;不重复则插入链表或红黑树。
⑤ 📝 扩容判断:当元素个数超过 数组容量 * 加载因子 时,数组会扩容为原来的 2 倍。
1.3.2 代码实操:HashMap 的常用操作
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("语文", 90);
hashMap.put("数学", 95);
hashMap.put("英语", 92);
hashMap.put("数学", 100);
System.out.println("HashMap 内容:" + hashMap);
Integer mathScore = hashMap.get("数学");
System.out.println("数学成绩:" + mathScore);
boolean hasEnglish = hashMap.containsKey("英语");
boolean has90 = hashMap.containsValue(90);
System.out.println("包含英语 Key:" + hasEnglish);
System.out.println("包含 90 分 Value:" + has90);
hashMap.remove("语文");
System.out.println("删除语文后的 HashMap:" + hashMap);
int size = hashMap.size();
System.out.println("HashMap 大小:" + size);
hashMap.clear();
System.out.println("清空后是否为空:" + hashMap.isEmpty());
}
}
HashMap 内容:{语文=90, 数学=100, 英语=92}
数学成绩:100
包含英语 Key:true
包含 90 分 Value:true
删除语文后的 HashMap:{数学=100, 英语=92}
HashMap 大小:2
清空后是否为空:true
1.3.3 自定义对象作为 Key 的注意事项
⚠️ 当使用自定义对象作为 HashMap 的 Key 时,必须重写 hashCode() 和 equals() 方法,否则无法保证 Key 的唯一性。
我们以 Student 类为例,实现基于学号的 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;
}
@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);
}
@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", "张三");
studentMap.put(s1, "一班");
studentMap.put(s2, "二班");
studentMap.put(s3, "三班");
studentMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}
Student{id='001', name='张三'}=三班
Student{id='002', name='李四'}=二班
1.3.4 HashMap 性能分析
- 增删查操作:理想情况下时间复杂度为
O(1),哈希冲突严重时会退化为 O(n),红黑树转换后为 O(log n)。
- 扩容机制:扩容时需要重新计算所有元素的下标,非常消耗性能,开发中建议提前指定初始容量,减少扩容次数。
- ⚠️ 注意事项:HashMap 是线程不安全的集合,多线程环境下使用会出现数据错乱或
ConcurrentModificationException 异常。
1.4 LinkedHashMap:有序的哈希表
1.4.1 LinkedHashMap 底层原理
💡 LinkedHashMap 是 HashMap 的子类,底层结构是 HashMap + 双向链表。
它通过双向链表维护键值对的插入顺序或访问顺序,保证遍历顺序与插入顺序一致,或者与最近访问顺序一致。
LinkedHashMap 的元素唯一性判断规则与 HashMap 完全相同。
1.4.2 代码实操 1:插入顺序模式(默认)
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));
}
}
1.4.3 代码实操 2:访问顺序模式
通过构造方法 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 可以开启访问顺序模式,最近访问的元素会被移到链表尾部。
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapAccessOrderDemo {
public static void main(String[] args) {
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));
linkedHashMap.get("a");
System.out.println("\n访问元素 a 后的顺序:");
linkedHashMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}
初始顺序: a=A b=B c=C
访问元素 a 后的顺序: b=B c=C a=A
✅ 核心结论:访问顺序模式的 LinkedHashMap 可以用来实现 LRU 缓存淘汰算法(最近最少使用淘汰)。
1.4.4 性能分析
- LinkedHashMap 的增删查效率略低于 HashMap,因为需要维护双向链表的节点引用。
- 适合需要有序遍历且高效查找的场景,例如缓存系统、配置参数存储等。
- ⚠️ 注意事项:LinkedHashMap 同样是线程不安全的集合。
1.5 TreeMap:基于红黑树的排序映射
1.5.1 TreeMap 底层原理
💡 TreeMap 的底层结构是红黑树,它会自动对 Key 进行排序,默认是升序排列。
TreeMap 不允许 Key 为 null,因为排序时会抛出空指针异常。
TreeMap 保证 Key 唯一性的方式是通过比较 Key 的大小,而不是 hashCode() 和 equals() 方法。
- 自然排序:Key 实现
Comparable 接口,重写 compareTo() 方法。
- 定制排序:创建 TreeMap 时传入
Comparator 比较器,自定义排序规则。
1.5.2 代码实操 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");
treeMap.forEach((key, value) -> System.out.println(key + "=" + value));
}
}
1.5.3 代码实操 2:定制排序
我们对字符串 Key 进行降序排列,通过 Comparator 实现定制排序:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapCustomSortDemo {
public static void main(String[] args) {
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));
}
}
1.5.4 性能分析
- 增删查操作:时间复杂度稳定为
O(log n),效率低于 HashMap,但支持有序遍历。
- 适合需要排序和范围查询的场景,例如排行榜、字典排序等。
- ⚠️ 注意事项:
- TreeMap 是线程不安全的集合。
- 存储自定义对象作为 Key 时,必须指定排序规则,否则会抛出
ClassCastException。
1.6 Hashtable:线程安全的哈希表
1.6.1 Hashtable 核心特性
💡 Hashtable 是 Map 接口的早期实现类,底层结构是 数组 + 链表(JDK8 没有红黑树优化)。
它的所有方法都添加了 synchronized 关键字,是线程安全的集合。
Hashtable 不允许 Key 或 Value 为 null,默认初始容量为 11,加载因子为 0.75。
1.6.2 性能分析
- Hashtable 的线程安全是通过方法加锁实现的,锁粒度大,并发性能低。
- 现代开发中,不推荐使用 Hashtable,优先使用 ConcurrentHashMap 实现线程安全。
1.7 ConcurrentHashMap:并发安全的哈希表
1.7.1 ConcurrentHashMap 核心特性
💡 ConcurrentHashMap 是 JUC 包下的线程安全集合,专门用于解决 HashMap 的并发问题。
JDK8 中 ConcurrentHashMap 的底层结构是 数组 + 链表 + 红黑树,与 HashMap 类似。
它采用分段锁或 CAS + synchronized 的方式实现线程安全,锁粒度小,并发性能远高于 Hashtable。
ConcurrentHashMap 支持 Key 和 Value 为 null(与 Hashtable 不同)。
1.7.2 代码实操: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<>();
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();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ConcurrentHashMap 大小:" + concurrentMap.size());
}
}
ConcurrentHashMap 大小:2000
✅ 核心结论:多线程环境下优先使用 ConcurrentHashMap,兼顾线程安全和并发性能。
1.8 实战案例:基于 Map 实现 LRU 缓存
1.8.1 需求分析
💡 实现一个 LRU(最近最少使用)缓存工具类,满足以下需求:
- 缓存容量有限,超出容量时自动淘汰最近最少使用的元素。
- 支持缓存的添加、查询、删除操作。
- 保证操作的时间复杂度尽可能低。
1.8.2 实现思路
利用 LinkedHashMap 的访问顺序模式实现 LRU 缓存,重写 removeEldestEntry() 方法,自定义淘汰规则。
1.8.3 代码实现
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxCapacity;
public LRUCache(int maxCapacity) {
super(16, 0.75f, true);
this.maxCapacity = maxCapacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
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);
cache.get("A");
System.out.println("访问 A 后的缓存:" + cache);
cache.put("D", 4);
System.out.println("添加 D 后的缓存:" + cache);
}
}
初始缓存:{A=1, B=2, C=3}
访问 A 后的缓存:{B=2, C=3, A=1}
添加 D 后的缓存:{C=3, A=1, D=4}
1.8.4 案例总结
✅ 这个 LRU 缓存工具类充分利用了 LinkedHashMap 的特性,代码简洁且性能高效。
通过重写 removeEldestEntry() 方法,轻松实现了缓存淘汰规则,在实际开发中可直接用于本地缓存场景。
1.9 本章总结
- Map 是键值对集合,Key 唯一,Value 可重复,常用实现类有 HashMap、LinkedHashMap、TreeMap。
- HashMap 底层是数组 + 链表 + 红黑树,查询效率高,适合快速查找场景,线程不安全。
- LinkedHashMap 基于 HashMap+ 双向链表,支持插入顺序或访问顺序遍历,可实现 LRU 缓存。
- TreeMap 底层是红黑树,支持 Key 排序,适合有序遍历和范围查询场景,线程不安全。
- 多线程环境下,优先使用 ConcurrentHashMap 保证线程安全,避免使用 Hashtable。
- 自定义对象作为 HashMap Key 时,必须重写
hashCode() 和 equals() 方法。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online