248. Java 集合 - 使用 Map 存储键值对
1. 🌟 引入:什么是 Map🗺️?
在 Java 中,Map 是集合框架(Collections Framework)提供的第二大核心结构,它实现了一种非常经典、基础的数据存储模型:哈希表(HashMap)结构。
这个概念并不新颖,早在计算机科学发展的早期,哈希表就已被广泛应用,用来高效地管理内存内外的数据。
2. 🧩 什么是哈希表(HashMap)?
哈希表的本质是:存储键值对(Key-Value Pair)。
- 键(
Key):用来标识或定位一个对象。 - 值(
Value):真正需要操作的数据对象。
✨ 一个简单的例子:
假设你正在开发一个发票管理系统,需要处理大量的发票(Invoice 类的实例)。
Value(值):每一张发票对象。Key(键):发票号(Invoice Number),通常是一个唯一的字符串或数字。
通过发票号(
key),就可以快速找到对应的发票信息(value)。
3. 🧠 Map 的核心特性总结
| 特性 | 说明 |
|---|---|
| 1️⃣ 存储键值对 | 每一对键和值绑定在一起,组成一个**Entry** |
| 2️⃣ 键通常是简单对象 | 如 String、Integer |
| 3️⃣ 值可以是任意复杂对象 | 比如对象、集合、甚至另一个 Map |
| 4️⃣ 键在 Map 中必须唯一 | 不允许重复键(Key) |
| 5️⃣ 值可以重复 | 不同键可以指向相同的值 |
| 6️⃣ 通过键可以快速检索对应的值 | 时间复杂度接近 O(1) |
4. 📚 Map 接口的继承体系
Map 体系结构非常清晰,类似于 Set 的继承关系:
MapSortedMap:保持键值对按键的自然顺序排序。NavigableMap:在SortedMap基础上,增加了更强大的导航方法(如查找小于/大于某个键的条目)。
5. 🛠️ Map 接口常用实现类
🟢 HashMap(最常用)
- 无序存储(即插入顺序不保证)。
- 快速检索。
- 允许
null键和多个null值。
Map<String, Invoice> invoiceMap = new HashMap<>();
invoiceMap.put("INV-001", new Invoice("INV-001", 1500));
Invoice invoice = invoiceMap.get("INV-001"); // 通过 key 获取值
🟡 LinkedHashMap(有序存储)
- 保持插入顺序。
- 比
HashMap略慢(因为需要维护顺序)。 - 适合对遍历顺序有要求的场景。
Map<String, String> map = new LinkedHashMap<>();
map.put("A", "Apple");
map.put("B", "Banana");
map.put("C", "Cherry");
System.out.println(map.keySet()); // 输出: [A, B, C]
🔴 IdentityHashMap(基于 == 比较)
- 极少使用!
- 比较键时,不是用
.equals(),而是直接用==。 - 只有在你明确知道自己需要基于内存地址比较时才使用。
IdentityHashMap<String, String> idMap = new IdentityHashMap<>();
idMap.put(new String("A"), "Apple");
idMap.put(new String("A"), "Another Apple");
System.out.println(idMap.size()); // 输出 2,因为两个 "A" 是不同的对象
6. 🎯 什么是 Multimap?
有时候我们希望一个键对应多个值,这就是**Multimap**的概念。
⚡ Java Collections Framework 本身不直接支持
Multimap。
但是可以通过**Map<K, List>**自己简单实现:
Map<String, List<String>> multiMap = new HashMap<>();
multiMap.computeIfAbsent("fruit", k -> new ArrayList<>()).add("Apple");
multiMap.computeIfAbsent("fruit", k -> new ArrayList<>()).add("Banana");
multiMap.computeIfAbsent("vegetable", k -> new ArrayList<>()).add("Carrot");
System.out.println(multiMap.get("fruit")); // 输出: [Apple, Banana]
✅ 这样就模拟出了一个键对应多个值的结构!
7. ⚡ 小结:使用 Map 的场景总结
| 应用场景 | 适合的实现 |
|---|---|
| 快速存取、无顺序要求 | HashMap |
| 需要保持插入顺序 | LinkedHashMap |
| 希望键对象按自然顺序排序(如数字、字母) | TreeMap(SortedMap实现) |
| 特殊情况下基于内存地址比较 | IdentityHashMap |
| 一个键需要关联多个值 | Map<K, List<V>> 手动模拟 |
👀 互动问题
问题1:如果要保持元素插入顺序,使用哪种 Map 实现?
答案示例:使用
LinkedHashMap。
问题2:在什么情况下可能需要 IdentityHashMap?
答案示例:在需要通过对象引用(==)判断相等,而不是通过
.equals()比较内容时。
问题3:Multimap 在 Java Collections 中直接支持吗?如果不支持,我们怎么实现?
答案示例:不直接支持,可以通过
Map<K, List<V>>结构模拟实现。