Java 集合框架:HashMap 与 Map 体系深度解析
Java-day14
引入
在 Java 集合框架中,Map 体系以“键值对(Key-Value)”的存储形式,成为处理“映射关系”的核心工具。其中,`HashMap` 凭借“高效的查询性能”和“灵活的使用场景”,成为 Map 体系中最常用的实现类。本文将围绕 `HashMap` 展开,从底层数据结构、核心方法到与其他 Map 实现的对比,全面解析其设计逻辑与实战技巧,帮助读者掌握“键值对”数据的高效处理方式。
一、HashMap 底层原理与核心特点
1.1 底层数据结构:数组 + 链表 + 红黑树(JDK 8+)
JDK 7 及之前:底层是数组 + 链表。数组(称为“桶”)存储链表的头节点,链表解决哈希冲突(不同 Key 哈希后落到同一桶)。
JDK 8 及之后:优化为**数组 + 链表 + 红黑树**。当链表长度超过阈值(默认 8)且数组长度≥64 时,链表会转换为**红黑树**(查询时间复杂度从 O(n) 优化为 O(logn))。
1.2 HashMap 核心特点
存储形式:以 Key-Value 键值对存储,Key 不允许重复(重复则覆盖 Value),Value 允许重复;
无序性:插入顺序与取出顺序不一致;
null 支持:Key 和 Value 都允许为 null,但 Key 只能有一个 null;
线程不安全:多线程环境下可能出现死循环、数据丢失等问题(需用 `ConcurrentHashMap` 替代)。
二、HashMap 核心方法与实战
import java.util.HashMap;import java.util.Map;public class HashMapDemo {public static void main(String[] args) {// 创建 HashMap 集合(泛型指定 Key 为 String,Value 为 Integer)HashMap<String, Integer> map = new HashMap<>();// 1. 添加键值对:put(K key, V value)map.put("Tom", 100);map.put("Jim", 90);map.put("Sam", 95);System.out.println("添加后:" + map); // 输出:{Tom=100, Jim=90, Sam=95}// Key 重复时,Value 会被覆盖map.put("Tom", 85);System.out.println("覆盖后:" + map); // 输出:{Tom=85, Jim=90, Sam=95}// 2. 获取 Value:get(Object key)Integer score = map.get("Jim");System.out.println("Jim 的分数:" + score); // 输出:90// 3. 获取键值对数量:size()System.out.println("键值对数量:" + map.size()); // 输出:3// 4. 清空集合:clear()// map.clear();// System.out.println("清空后大小:" + map.size()); // 输出:0// 5. 判断是否为空:isEmpty()System.out.println("是否为空:" + map.isEmpty()); // 输出:false// 6. 删除键值对:remove(Object key)map.remove("Sam");System.out.println("删除后:" + map); // 输出:{Tom=85, Jim=90}// 7. 判断是否包含 Key/Value:containsKey()、containsValue()System.out.println("是否包含 Key 'Tom':" + map.containsKey("Tom")); // 输出:trueSystem.out.println("是否包含 Value 90:" + map.containsValue(90)); // 输出:true// 8. 批量添加键值对:putAll(Map<? extends K, ? extends V> m)HashMap<String, Integer> map2 = new HashMap<>();map2.put("Lily", 92);map2.put("Lucy", 88);map.putAll(map2);System.out.println("批量添加后:" + map); // 输出:{Tom=85, Jim=90, Lily=92, Lucy=88}// 9. 替换 Value:replace(K key, V value)map.replace("Lily", 95);System.out.println("替换后:" + map); // 输出:{Tom=85, Jim=90, Lily=95, Lucy=88}
// 10. 遍历 HashMap(三种方式)// 方式1:遍历 Key 集合System.out.print("遍历 Key:");for (String key : map.keySet()) {System.out.print(key + " "); // 输出:Tom Jim Lily Lucy}System.out.println();
// 方式2:遍历 Value 集合System.out.print("遍历 Value:");for (Integer val : map.values()) {System.out.print(val + " "); // 输出:85 90 95 88}System.out.println();
// 方式3:遍历 Entry 键值对System.out.println("遍历 Entry:");for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + " = " + entry.getValue());}}}三、TreeMap:有序的 Map 实现
3.1 TreeMap 底层原理与特点
数据结构:基于红黑树实现,自动对 Key 进行排序;
排序规则:
默认按 Key 的自然顺序排序(如 `Integer` 升序、`String` 字母序);
可通过构造函数指定自定义比较器(Comparator)。
3.2 TreeMap 核心用法示例
import java.util.Map;import java.util.TreeMap;public class TreeMapDemo {public static void main(String[] args) {// 自然排序(Integer 升序)TreeMap<Integer, String> treeMap1 = new TreeMap<>();treeMap1.put(3, "C");treeMap1.put(1, "A");treeMap1.put(2, "B");System.out.println("自然排序:" + treeMap1); // 输出:{1=A, 2=B, 3=C}// 自然排序(String 字母序)TreeMap<String, Integer> treeMap2 = new TreeMap<>();treeMap2.put("orange", 3);treeMap2.put("apple", 1);treeMap2.put("pear", 2);System.out.println("自然排序:" + treeMap2); // 输出:{apple=1, orange=3, pear=2}// 自定义比较器(Person 按姓名长度排序)TreeMap<Person, String> treeMap3 = new TreeMap<>((p1, p2) -> {return Integer.compare(p1.name.length(), p2.name.length());});treeMap3.put(new Person("Tom"), "A");treeMap3.put(new Person("Bob"), "B");treeMap3.put(new Person("Lily"), "C");for (Person p : treeMap3.keySet()) {System.out.println(p.name); // 输出:Bob Tom Lily}}}class Person {String name;Person(String name) {this.name = name;}}四、Hashtable:遗留的线程安全 Map 实现
4.1 Hashtable 特点与局限
线程安全:所有方法加 `synchronized` 同步锁,多线程环境下安全;
限制:Key 和 Value 都不允许为 null,否则抛 `NullPointerException`;
性能:因同步锁导致性能低于 `HashMap`;
现状:属于 Java 遗留类,不建议在新代码中使用(可改用 `ConcurrentHashMap`)。
4.2 Hashtable 与 HashMap 核心区别
| 特性 | HashMap | Hashtable |
|---|---|---|
| 线程安全 | 不安全 | 安全(同步锁) |
| null 支持 | Key/Value 可空(Key 仅一个 null) | 不支持 null |
| 初始容量 | 16 | 11 |
| 扩容机制 | 原容量 × 1.5 | 原容量 × 2 + 1 |
| 迭代器 | 快速失败(fail-fast) | 安全失败(fail-safe) |
| 性能 | 高 | 低(同步开销) |
五、Map 集合选型建议
| 集合类 | 底层结构 | 线程安全 | 排序性 | null 支持 | 适用场景 |
|---|---|---|---|---|---|
| HashMap | 数组 + 链表 + 红黑树 | 不安全 | 无序 | 支持 | 通用键值对存储,查询频繁场景 |
| TreeMap | 红黑树 | 不安全 | 有序 | 支持 | 需按键排序的场景 |
| Hashtable | 数组 + 链表 | 安全 | 无序 | 不支持 | 遗留系统维护(不推荐新用) |
| ConcurrentHashMap | 分段锁 + 数组 + 链表 + 红黑树 | 安全 | 无序 | 支持 | 多线程键值对存储 |
六、总结:HashMap 与 Map 体系核心要点
1.HashMap:
底层是“数组 + 链表 + 红黑树”,查询效率高(O(1) 到 O(logn));
掌握 `put()`、`get()`、`remove()`、`containsKey()` 等核心方法,以及三种遍历方式;
线程不安全,多线程场景需改用 `ConcurrentHashMap`。
2.TreeMap:
基于红黑树,自动按键排序;
适合需“有序键”的场景,如排行榜、字典序存储。
3.Hashtable:
- 线程安全但性能低,属于遗留类,新代码中避免使用。
4.选型建议:
优先选 `HashMap`(性能优、场景通用);
需排序选 `TreeMap`;
多线程选 `ConcurrentHashMap`。
通过本文的学习,相信你已对 Java Map 体系尤其是 `HashMap` 有了全面理解。在实际开发中,需根据“线程安全需求”“排序需求”“null 支持需求”等因素选择合适的 Map 实现,以达到功能与性能的平衡。
