一、HashMap核心概念与数据结构
1.1 HashMap示意图解析
HashMap是Java集合框架中最重要且最常用的Map实现类,其底层采用精妙的数据结构设计:
JDK 8之前的数据结构:
数组 + 链表
0\] -\> null \[1\] -\> key:17 → value:34 → next \[2\] -\> key:12 → value:24 → next \[3\] -\> key:55 → value:34 → next \[4\] -\> null ...
JDK 8及之后的数据结构:

1.2 HashMap的本质
HashMap本质上是一个"数组 + 链表/红黑树"的复合数据结构:
- 数组:作为哈希桶,存储链表的头节点或红黑树的根节点
- 链表:解决哈希冲突,将哈希值相同的元素链接在一起
- 红黑树:当链表过长时转换为红黑树,提高查询效率
二、HashMap的核心特点
2.1 基本特性
- 键值对存储:存储key-value类型的数据
- 键唯一性:key不允许重复,重复的key会覆盖原有value
- 值可重复:value允许重复
- 存储无序:插入顺序与遍历顺序不一致
- 允许null:key和value都允许为null,但只能有一个null key
2.2 底层机制详解
哈希函数机制:
java
// HashMap计算哈希值的关键代码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
扩容机制:
- 默认初始容量:16
- 负载因子:0.75(当元素数量达到容量的75%时触发扩容)
- 扩容策略:容量变为原来的2倍
树化条件: - 链表长度 ≥ 8
- 数组长度 ≥ 64
- 同时满足以上两个条件时,链表转换为红黑树
三、HashMap常用方法实战详解
3.1 基础操作示例
① put(K key, V value) - 添加键值对
java
import java.util.HashMap;
public class HashMapBasic {
public static void main(String[] args) {
// 创建HashMap实例
HashMap<String, Integer> map = new HashMap<String, Integer>();
// 添加键值对
map.put("Tom", 100);
map.put("Jerry", 95);
map.put("Alice", 88);
System.out.println("初始HashMap: " + map);
// key重复的情况 - 覆盖原有值
map.put("Tom", 0); // 覆盖Tom原来的分数100
System.out.println("覆盖后HashMap: " + map);
// 允许null值和null键
map.put(null, 60); // null键
map.put("Nobody", null); // null值
System.out.println("包含null的HashMap: " + map);
}
}
② get(Object key) - 获取值
java
public class HashMapGet {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom", 100);
map.put("Jim", 90);
map.put("Sam", 91);
// 获取存在的key对应的value
int tomScore = map.get("Tom");
System.out.println("Tom的分数: " + tomScore);
// 获取不存在的key - 返回null
Integer unknownScore = map.get("Unknown");
System.out.println("不存在的key返回值: " + unknownScore);
// 处理可能为null的返回值
String key = "Bob";
Integer score = map.get(key);
if (score != null) {
System.out.println(key + "的分数: " + score);
} else {
System.out.println(key + "不存在于Map中");
}
}
}
③ size() - 获取元素数量
java
public class HashMapSize {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
System.out.println("初始大小: " + map.size());
map.put("Tom", 100);
map.put("Jim", 90);
map.put("Sam", 91);
System.out.println("添加3个元素后大小: " + map.size());
// size()返回的是key-value对的数量,不是容量
System.out.println("当前Map: " + map);
System.out.println("元素数量: " + map.size());
}
}
3.2 清空与状态检查
④ clear() - 清空集合
java
public class HashMapClear {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom", 100);
map.put("Jim", 90);
map.put("Sam", 91);
System.out.println("清空前: " + map);
System.out.println("清前前大小: " + map.size());
// 清空所有元素
map.clear();
System.out.println("清空后: " + map);
System.out.println("清空后大小: " + map.size());
}
}
⑤ isEmpty() - 判断是否为空
java
public class HashMapIsEmpty {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
System.out.println("初始是否为空: " + map.isEmpty());
map.put("Tom", 100);
map.put("Jim", 90);
System.out.println("添加元素后是否为空: " + map.isEmpty());
map.clear();
System.out.println("清空后是否为空: " + map.isEmpty());
// 实际应用场景
if (map.isEmpty()) {
System.out.println("Map为空,需要初始化数据");
} else {
System.out.println("Map中有数据,可以进行操作");
}
}
}
3.3 删除操作
⑥ remove(Object key) - 删除指定键
java
public class HashMapRemove {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom", 100);
map.put("Jim", 90);
map.put("Sam", 91);
map.put("Alice", 85);
System.out.println("删除前: " + map);
// 删除存在的key
Integer removedValue = map.remove("Tom");
System.out.println("删除Tom,返回值: " + removedValue);
System.out.println("删除后: " + map);
// 删除不存在的key
Integer notExist = map.remove("Unknown");
System.out.println("删除不存在的key,返回值: " + notExist);
// 删除另一个元素
map.remove("Jim");
System.out.println("最终Map: " + map);
}
}
3.4 存在性检查
⑦ containsKey(Object key) - 检查键是否存在
java
public class HashMapContainsKey {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
// 检查不存在的key
System.out.println("检查DEMO是否存在: " + map.containsKey("DEMO")); // false
// 添加元素后检查
map.put("DEMO", 1);
map.put("Tom", 100);
map.put(null, 50); // 包含null键
System.out.println("添加后检查DEMO: " + map.containsKey("DEMO")); // true
System.out.println("检查Tom: " + map.containsKey("Tom")); // true
System.out.println("检查null键: " + map.containsKey(null)); // true
System.out.println("检查不存在的key: " + map.containsKey("Unknown")); // false
// 实际应用:避免空指针异常
String key = "SomeKey";
if (map.containsKey(key)) {
Integer value = map.get(key);
System.out.println(key + " 的值是: " + value);
} else {
System.out.println(key + " 不存在于Map中");
}
}
}
⑧ containsValue(Object value) - 检查值是否存在
java
public class HashMapContainsValue {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
// 检查不存在的value
System.out.println("检查value=1是否存在: " + map.containsValue(1)); // false
// 添加元素后检查
map.put("DEMO", 1);
map.put("Tom", 100);
map.put("Alice", 100); // 重复的value
map.put("Sam", null); // null值
System.out.println("检查value=1: " + map.containsValue(1)); // true
System.out.println("检查value=100: " + map.containsValue(100)); // true
System.out.println("检查null值: " + map.containsValue(null)); // true
System.out.println("检查不存在的value: " + map.containsValue(999)); // false
// 实际应用:查找特定值的键
int targetValue = 100;
if (map.containsValue(targetValue)) {
System.out.println("存在值为 " + targetValue + " 的键值对");
// 遍历查找具体是哪些key
for (HashMap.Entry<String, Integer> entry : map.entrySet()) {
if (targetValue == entry.getValue()) {
System.out.println("键: " + entry.getKey() + ", 值: " + entry.getValue());
}
}
}
}
}
3.5 批量操作
⑨ putAll(Map m) - 合并Map
java
public class HashMapPutAll {
public static void main(String[] args) {
HashMap<String, Integer> map1 = new HashMap<>();
HashMap<String, Integer> map2 = new HashMap<>();
// 初始化两个Map
map1.put("DEMO1", 1);
map1.put("DEMO2", 2);
map2.put("DEMO3", 3);
map2.put("DEMO4", 4);
map2.put("DEMO1", 100); // 重复的key,会被覆盖
System.out.println("map1初始内容: " + map1);
System.out.println("map2初始内容: " + map2);
// 将map2的所有元素添加到map1中
map1.putAll(map2);
System.out.println("合并后map1: " + map1);
System.out.println("注意: DEMO1的值被覆盖为100");
// 实际应用场景:合并配置或数据
HashMap<String, Integer> defaultConfig = new HashMap<>();
defaultConfig.put("timeout", 30);
defaultConfig.put("retries", 3);
HashMap<String, Integer> userConfig = new HashMap<>();
userConfig.put("timeout", 60); // 用户自定义超时
userConfig.put("max_connections", 100);
// 合并配置,用户配置优先
defaultConfig.putAll(userConfig);
System.out.println("最终配置: " + defaultConfig);
}
}
3.6 替换操作
⑩ replace(K key, V value) - 替换值
java
public class HashMapReplace {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("DEMO1", 1);
map.put("DEMO2", 2);
map.put("DEMO3", 3);
System.out.println("替换前: " + map);
// 替换存在的key
Integer oldValue = map.replace("DEMO2", 200);
System.out.println("DEMO2的旧值: " + oldValue);
System.out.println("替换后: " + map);
// 替换不存在的key - 返回null,Map不变
Integer notExist = map.replace("UNKNOWN", 999);
System.out.println("替换不存在的key返回值: " + notExist);
System.out.println("Map内容未变化: " + map);
// 条件替换:只有当前值与期望值相等时才替换
boolean replaced = map.replace("DEMO1", 1, 1000);
System.out.println("条件替换是否成功: " + replaced);
System.out.println("条件替换后: " + map);
// 条件替换失败的情况
boolean failed = map.replace("DEMO1", 1, 2000);
System.out.println("条件替换失败: " + failed);
}
}
四、HashMap高级特性与最佳实践
4.1 遍历HashMap的多种方式
java
import java.util.HashMap;
import java.util.Map;
public class HashMapIteration {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("Tom", 100);
map.put("Jerry", 95);
map.put("Alice", 88);
map.put("Bob", 92);
System.out.println("=== 多种遍历方式 ===");
// 1. 遍历键集合
System.out.println("1. 遍历键:");
for (String key : map.keySet()) {
System.out.println("Key: " + key + ", Value: " + map.get(key));
}
// 2. 遍历值集合
System.out.println("\n2. 遍历值:");
for (Integer value : map.values()) {
System.out.println("Value: " + value);
}
// 3. 遍历键值对集合(推荐)
System.out.println("\n3. 遍历键值对:");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
// 4. 使用forEach方法(Java 8+)
System.out.println("\n4. 使用forEach:");
map.forEach((key, value) ->
System.out.println("Key: " + key + ", Value: " + value));
}
}
4.2 性能优化建议
java
public class HashMapOptimization {
public static void main(String[] args) {
// 1. 预估容量,避免频繁扩容
int expectedSize = 1000;
HashMap<String, Integer> optimizedMap = new HashMap<>(
(int)(expectedSize / 0.75) + 1
);
// 2. 使用合适的hashCode方法
class Student {
String name;
int id;
Student(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public int hashCode() {
return Objects.hash(name, id); // 良好的hashCode分布
}
@Override
public boolean equals(Object obj) {
// 必须重写equals方法
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student student = (Student) obj;
return id == student.id && Objects.equals(name, student.name);
}
}
}
}
五、总结
HashMap作为Java中最常用的Map实现,其核心特点包括:
- 数据结构:数组 + 链表/红黑树,平衡查询效率与空间使用
- 键唯一性:重复key会覆盖原有value
- 存储无序:不保证元素的插入顺序
- 允许null:支持null键和null值
- 自动扩容:根据负载因子动态调整容量
通过掌握HashMap的各种操作方法及其底层原理,开发者可以更加高效地使用这一重要的数据结构,并在适当的场景下选择合适的Map实现。在实际开发中,应根据数据特性和访问模式来决定是否使用HashMap,以及如何优化其性能。