HashMap知识点

一、HashMap 核心定义

HashMap 是 Java 集合框架中 Map 接口的哈希表实现 ,位于 java.util 包下,用于存储「键值对(Key-Value)」;允许 KeyValuenull(仅一个 Keynull),线程不安全,元素无序(不保证插入 / 遍历顺序)。

二、底层原理(JDK 8 核心)

HashMap 底层是「数组 + 链表 + 红黑树」的复合结构,核心设计是为了解决哈希冲突,提升查询 / 增删效率:

  1. 数组(哈希桶,table)
    • 数组类型为 Node<K,V>[](链表节点),每个数组下标对应一个「哈希桶」,桶中存储链表 / 红黑树;
    • 数组默认初始容量为 16,容量必须是 2 的幂(便于通过 hash & (length-1) 替代取模,提升计算效率)。
  2. 链表(解决哈希冲突)
    • 当多个 Key 的哈希值映射到同一个数组下标时,会以链表形式存储(JDK 7 头插法,JDK 8 尾插法,避免扩容时链表成环);
    • 链表节点 Node 包含 4 个属性:hash(Key 的哈希值)、keyvaluenext(后继节点)。
  3. 红黑树(优化链表性能)
    • 当链表长度 ≥ 8 且数组容量 ≥ 64 时,链表转为红黑树(降低查询时间复杂度从 O (n) 到 O (logn));
    • 当红黑树节点数 ≤ 6 时,转回链表(红黑树维护成本高,少量节点时链表更高效)。

三、核心机制(面试重中之重)

1. 哈希计算与索引定位
复制代码
graph LR
A[Key] --> B[计算hashCode()]
B --> C[扰动函数:hash = hashCode() ^ (hashCode() >>> 16)]
C --> D[索引计算:index = hash & (table.length - 1)]
  • 扰动函数 :JDK 8 中通过 hash ^ (hash >>> 16) 混合哈希码的高位和低位,减少哈希冲突(避免高位特征未被利用);
  • 索引计算 :用 hash & (length-1) 替代 hash % length(仅当容量为 2 的幂时等价),位运算效率更高。
2. 扩容机制(resize)
  • 触发条件
    1. 元素个数(size)≥ 阈值(threshold = 容量 × 负载因子);
    2. 链表转红黑树前,发现数组容量 < 64,先扩容而非转树。
  • 扩容流程
    1. 新容量 = 原容量 × 2(保持 2 的幂);
    2. 重新计算每个节点的索引(JDK 8 优化:只需判断哈希值的新增位是 0 还是 1,无需重新计算 hash);
    3. 将原数组节点迁移到新数组(链表 / 红黑树拆分);
  • 默认参数:负载因子(loadFactor)默认 0.75(平衡空间与时间:过小扩容频繁,过大哈希冲突概率高)。
3. 元素插入流程(JDK 8)
复制代码
graph TD
A[put(Key, Value)] --> B[计算Key的hash值]
B --> C[计算索引index = hash & (length-1)]
C --> D{桶是否为空?}
D -->|是| E[新建Node放入桶中]
D -->|否| F{桶中是红黑树?}
F -->|是| G[红黑树插入节点]
F -->|否| H{Key是否已存在?}
H -->|是| I[替换Value]
H -->|否| J[链表尾部插入节点]
J --> K{链表长度≥8且容量≥64?}
K -->|是| L[链表转红黑树]
K -->|否| M[插入完成]

四、核心特性

表格

特性 说明
线程安全 线程不安全(无同步锁),多线程并发修改可能导致死循环(JDK 7)/ 数据丢失,需用 ConcurrentHashMap
允许 null 值 Key 仅允许一个 null(哈希值固定为 0),Value 可多个 null
有序性 无序(不保证插入顺序与遍历顺序一致),需有序则用 LinkedHashMap
时间复杂度 理想情况(无哈希冲突):增删改查 O (1);最坏情况(全冲突):链表 O (n)、红黑树 O (logn)
初始容量 / 负载因子 默认容量 16,负载因子 0.75,可通过构造方法指定(建议初始化时指定容量,避免多次扩容)

五、常用核心方法

java

运行

复制代码
// 1. 初始化
HashMap<String, Integer> map = new HashMap<>(); // 默认容量16,负载因子0.75
HashMap<String, Integer> map2 = new HashMap<>(32); // 指定初始容量(会自动调整为2的幂)
HashMap<String, Integer> map3 = new HashMap<>(32, 0.8f); // 指定容量和负载因子

// 2. 增删改查
map.put("java", 1); // 插入键值对(Key存在则替换Value)
map.putIfAbsent("python", 2); // Key不存在时才插入
Integer val = map.get("java"); // 获取Value(Key不存在返回null)
map.remove("python"); // 删除键值对
boolean exists = map.containsKey("java"); // 判断Key是否存在
map.replace("java", 10); // 替换Value(Key存在才生效)

// 3. 遍历(推荐方式)
// 方式1:遍历KeySet
for (String key : map.keySet()) {
    System.out.println(key + ":" + map.get(key));
}
// 方式2:遍历EntrySet(效率更高,避免二次get)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 方式3:Lambda遍历
map.forEach((k, v) -> System.out.println(k + ":" + v));

// 4. 其他常用
int size = map.size(); // 获取元素个数
map.clear(); // 清空所有元素
Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); // 获取所有键值对

六、面试高频考点

1. JDK 7 vs JDK 8 核心差异

表格

维度 JDK 7 JDK 8
底层结构 数组 + 链表 数组 + 链表 + 红黑树
插入方式 头插法(扩容易成环) 尾插法(避免成环)
哈希计算 多次位运算 / 异或(复杂) 简化为 hash ^ (hash>>> 16)
扩容后索引计算 重新计算 hash → 取模 按哈希值新增位判断(0/1)
性能 链表查询 O (n) 红黑树查询 O (logn)
2. 易错点
  • Key 的 hashCode 和 equals 重写
    • 必须同时重写 hashCode()equals()(否则可能导致 Key 重复存储);
    • 规则:equals() 相等的 Key,hashCode() 必须相等;hashCode() 相等的 Key,equals() 不一定相等。
  • 并发问题
    • JDK 7 多线程扩容时,链表头插法可能导致死循环;
    • JDK 8 解决了死循环,但仍会出现数据丢失 / 覆盖,并发场景优先用 ConcurrentHashMap
  • null Key 处理
    • null Key 的哈希值固定为 0,索引为 0,因此仅能有一个 null Key。
  • 初始容量选择
    • 若预估存储 1000 个元素,建议初始化容量为 1000 / 0.75 ≈ 1334,再向上取最近的 2 的幂(2048),避免扩容。
3. HashMap vs Hashtable 对比

表格

维度 HashMap Hashtable
线程安全 不安全 安全(方法加 synchronized)
null 支持 Key/Value 可 null 不支持 null
性能 高(无锁) 低(全表锁)
底层结构 数组 + 链表 + 红黑树(JDK8) 数组 + 链表
初始容量 16 11
扩容方式 2 倍 2 倍 + 1

七、使用场景

  • 适合单线程、频繁增删改查的键值对存储场景;
  • 适合对顺序无要求、追求查询效率的场景;
  • 不适合并发场景 (用 ConcurrentHashMap)、有序场景(用 LinkedHashMap)。

总结

HashMap 核心关键点:

  1. JDK 8 底层是数组 + 链表 + 红黑树,哈希计算用扰动函数 + 位运算,扩容保持容量为 2 的幂;
  2. 红黑树阈值(链表≥8 转树,树≤6 转链表),负载因子默认 0.75,平衡空间与效率;
  3. 线程不安全,Key 需重写 hashCode/equals,并发场景用 ConcurrentHashMap。
相关推荐
顺风尿一寸2 小时前
Spring事务回滚探秘:从@Transactional到数据库连接的完整旅程
java·后端
焦糖玛奇朵婷2 小时前
盲盒小程序一站式开发
java·大数据·服务器·前端·小程序
yatum_20142 小时前
VirtualBox 搭建 Hadoop-2.7.3 集群完整安装总结
java·ide·eclipse
@OuYang2 小时前
android10 应用安装
开发语言·python
番茄去哪了2 小时前
高并发选课系统页面阻塞现象的技术原理分析:同步交互与悲观锁机制
java·缓存·科普·面向对象编程
_MyFavorite_2 小时前
Python 中通过命令行向函数传参
开发语言·chrome·python
yujunl2 小时前
Net Core8项目不能正常发布
开发语言
lly2024062 小时前
JavaScript Window History
开发语言
程序员Terry2 小时前
别老写重复代码了!模版方法模式一次讲透
java·设计模式