Skip to content
第 14 章 后端 ⏱ 10 分钟阅读

第 14 章:Map 接口与 HashMap ​

学习目标 ​

  • 掌握 HashMap 的常用 API
  • 理解 key 的 hashCode + equals 契约
  • 学会 4 种遍历方式

一、Map:键值对 ​

java
Map<String, Integer> map = new HashMap<>();

map.put("apple", 1);                // ① 添加/覆盖
map.get("apple");                    // ② 1(不存在返回 null)
map.getOrDefault("orange", 0);       // ③ 0(不存在返回默认值)
map.containsKey("apple");            // ④ true
map.containsValue(1);                // ⑤ true
map.remove("apple");                 // ⑥ 删除(返回被删的 value)
map.size();
map.isEmpty();
map.clear();

二、4 种遍历方式 ​

java
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);

// ① 遍历键(需要 keys 时)
for (String key : map.keySet()) {
    System.out.println(key + " = " + map.get(key));
}

// ② 遍历值
for (Integer value : map.values()) {
    System.out.println(value);
}

// ③ 遍历 entry(键值对,推荐)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
}

// ④ forEach + Lambda(Java 8+)
map.forEach((k, v) -> System.out.println(k + " = " + v));

三、key 的 hashCode + equals 契约 ​

HashMap 用 key 的 hashCode 定位桶,用 equals 比较键是否相同。

⚠️ 坑 1:自定义对象做 key,必须重写 hashCode 和 equals,否则 get() 找不到值。

java
Map<User, String> map = new HashMap<>();
User u = new User("张三", 18);
map.put(u, "value");
map.get(new User("张三", 18));   // ❌ null(因为 hashCode 不同)

⚠️ 坑 2:可变对象不能做 key。如果 key 的字段改了,hashCode 也变了,就找不到了。

java
// String 不可变,所以能做 key
// 自己写个 MutableUser 改了 name,就 get 不到了

四、Map 实现类对比 ​

实现底层有序线程安全适用
HashMap数组+链表+红黑树❌❌99% 场景
LinkedHashMap链表+HashMap✅ 插入序❌要保留插入顺序
TreeMap红黑树✅ 键排序❌要按 key 排序
ConcurrentHashMap分段锁/CAS❌✅多线程场景
java
// 默认
Map<String, Integer> map = new HashMap<>();

// 多线程并发(后续章节详解)
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();

五、实战:用 Map 统计频次 ​

java
String text = "hello world hello java hello";
Map<String, Integer> wordCount = new HashMap<>();

for (String word : text.split(" ")) {
    wordCount.merge(word, 1, Integer::sum);   // 不存在则 put 1,存在则 +1
}

// 输出:{hello=3, world=1, java=1}
System.out.println(wordCount);

⚠️ 坑 3:merge 是最优雅的累加写法,不要写 if (containsKey) { +1 } else { put 1 } 这种啰嗦代码。

六、HashMap 性能小技巧 ​

预分配容量(知道大概会放多少元素):

java
// 默认容量 16,放多了会扩容(重新哈希,慢)
Map<String, Integer> map = new HashMap<>(1000);   // 预分配 1000 容量

loadFactor 一般不动,默认 0.75 是平衡点。

七、本章小结 ​

要点关键
Map 特性键值对,key 不可重复
key 契约必须重写 hashCode + equals,不可变
遍历entrySet() 最常用
选型HashMap(默认)/ LinkedHashMap(有序)/ ConcurrentHashMap(线程安全)
merge累加的最优雅写法

动手练习 ​

  1. 字符频次统计:统计字符串中每个字符出现的次数
  2. 两数之和:给定数组和 target,找两个数的下标使和等于 target(用 HashMap)
  3. 按 value 排序:把 Map<String, Integer> 按 value 降序排序后输出

下一章:第 15 章:Collections 工具类与常用技巧 →

本站基于 VitePress 构建 · 由 StackHub 团队维护