HashMap 的原理是什么?
未标记
考查重点:哈希定位、冲突与扩容。哈希定位桶,桶内比键处理冲突,负载增大时扩容;Java HashMap 链表达条件可树化,线程安全需另外保证
本题目录
参考答案
核心回答
以 OpenJDK 25 的 java.util.HashMap 为例,它用“数组定位桶、桶内比较键、随负载扩容”实现键值映射。桶中的冲突节点通常组成链表,符合条件时可转换为红黑树。
- 定位桶:取键的
hashCode(),将高位与低位混合得到内部hash,再用hash & (容量 - 1)定位数组下标。数组容量保持为 2 的幂,便于定位和扩容迁移。 - 处理冲突:落在同一桶不代表是同一个键,还要比较保存的
hash及键的引用或equals()。相同键更新值,不同键继续在链表或树中查找、插入。 - 查询:按同样方式定位桶,再找匹配的键。哈希分布良好时,
get、put通常具有平均常数时间的表现;冲突严重时会变慢。 - 扩容:新增映射超过容量与负载因子对应的阈值后,通常把数组扩为两倍并重新分配桶内节点,降低拥挤程度。
HashMap 允许一个 null 键和多个 null 值,不保证遍历顺序,也不提供并发修改的线程安全保证。树化改善冲突严重时的查找,不能替代正确的哈希设计和并发控制。
为什么既要 hash,又要 equals?
hash 用来缩小查找范围,equals() 才决定两个不同对象在业务上是否代表同一个键。两个键的 hash 相同,可能只是哈希碰撞;即使内部 hash 不同,也可能因数组只取其中部分位而进入同一个桶。
因此,自定义键需要遵守 equals() 与 hashCode() 的约定:相等的对象必须具有相同的 hashCode,不相等的对象允许碰撞。 只重写 equals() 而没有相应重写 hashCode(),可能使逻辑相等的键落入不同位置,破坏预期的查找和去重行为。
键存入后,也不应再修改参与这两个方法判断的字段。节点保存的是插入时算出的 hash,后来字段变化可能使查询去另一个桶查找;对象还在 Map 中,却不能按原来的预期找到。扩容也不会替应用修复可变键的语义。
链表、红黑树和扩容各解决什么?
链表让多个不同键能够共存在同一桶中,但查找可能要逐个比较。冲突链较长时,实现会尝试树化:表较小时优先扩容,让节点有机会分散到更多桶;表已经足够大,才把相应桶转成红黑树,以控制长链带来的查找开销。树节点结构更复杂,所以不会从一开始就把所有桶都建成树。
扩容关注整个表的负载,树化关注某一个桶的严重冲突,两者不是同一个触发条件。默认负载因子是 0.75;例如数组容量为 16 时,常见阈值是 12。这里的容量指桶数,size 指键值映射数量,不是数组里非空桶的数量;默认构造也通常将实际数组分配留到首次插入时。
容量翻倍后,链表节点利用已保存的 hash 拆成两组,不必重新调用每个键的 hashCode():节点要么保留在原下标,要么移动到“原下标 + 旧容量”。决定因素是 hash 中与旧容量对应的那一位。
例如旧容量为 16,内部 hash 为 3 和 19 的两个键都在下标 3;扩到 32 后,前者仍在 3,后者移到 19。扩容减少了这两个键的桶冲突,但仍要迁移节点并分配新数组,所以已知规模时可以合理预估初始容量,减少反复扩容,也避免过度预分配。
常见追问
变成红黑树后就一定很快吗? 仍依赖键的 hash 和比较性质。树结构有助于处理长链,但大量相同 hash、键又难以区分等情况仍可能增加搜索工作,不能把任何输入下的查询都保证为常数或对数时间。
为什么不能直接让多个线程同时写? HashMap 的节点连接、数组替换、计数等操作没有统一的并发协调,可能出现覆盖、丢失更新或遍历异常。需要并发访问时,可使用合适的并发 Map 或外部同步;涉及“先判断再更新”的复合业务操作,还要明确整体的原子性。
遍历抛出 ConcurrentModificationException 就算并发保护吗? 不是。fail-fast 是尽力发现不合规结构修改的诊断机制,不保证每次都能检测到,也不能用它建立线程安全。
参考资料
- 国内:JavaGuide:HashMap 源码分析:借鉴桶定位、put/get 与扩容的流程,说明正文中查询写入以及容量、映射数量和负载阈值的区别。
- 国内:美团技术团队:Java 8 系列之重新认识 HashMap:存储结构与索引计算部分解释数组、链表和红黑树怎样处理冲突,以及容量采用 2 的幂的作用。
- 国内:沉默王二:Java HashMap 详解:参考 hash 计算与扩容示例,解释正文中桶掩码、高低位混合和容量翻倍后节点分散的机制。
- 国外:Java SE 25:HashMap:官方性能、负载因子、顺序和同步约定说明 HashMap 提供什么保证,以及 fail-fast 为什么不是并发控制。
- 国外:Java SE 25:Object:
equals()与hashCode()的契约说明如何正确实现自定义键,以及相等与哈希碰撞之间的区别。
相关面经
51 篇公司未注明
后端开发问一下你的Java基础,说一下hashmap底层,hashmap为什么线程不安全
公司未注明
后端开发工程师Java中HashMap的底层数据结构是什么?还有它的扩容机制是什么样的?在哪个JDK版本有改变的?
小红书
国际化开发工程师● HashMap的实现机制
水滴公司
AI 全栈工程师那比如 Java 的 HashMap 这个数据结构,有了解过吗?
钛松科技
后端开发工程师HashMap 底层原理是什么?
展开剩余 46 篇面经
网易云音乐
全栈HashMap 它是一个怎么样的结构?
乐堡互娱
AI应用开发岗HashMap、重载和重写的区别
BIGO
客户端开发HashMap/TreeMap、复杂度、哈希冲突、负载因子、选型
九方智投
Agent 开发工程师hashmap说一下
百度
后端开发工程师HashMap底层实现原理是什么?数据结构是怎么样的?是否线程安全?不使用ConcurrentHashMap、HashTable这种已有实现,你会如何改进现有的HashMap实现线程安全?
蚂蚁集团
说下 HashMap 的原理,JDK1.8 里 HashMap 做了什么优化?这些优化解决了之前的什么问题?另外它的并发安全问题怎么解?
深信服
Java 开发1,讲讲hashmap底层
趣链科技
去哪儿旅行
AI智能应用开发当两个key的hash值冲突的时候,是怎么去决定存放位置的?
浪潮
智能体开发我看你有java经历,你了解hashmap吗,如何解决他的问题()
阿里巴巴
Java 研发工程师讲讲Java里hashmap的底层结构
益普科技
Java 开发了解hashmap吗
哔哩哔哩、百度、腾讯
知乎
全栈开发工程师阿里巴巴
AI 应用开发工程师hashmap原理
百度
后端开发工程师HashMap底层数据结构实现?链表转换为红黑树的触发条件是什么? 是线程安全的吗?
百度
后端开发工程师10 hashmap时间复杂度 底层实现
阿里巴巴
AI 应用开发工程师HashMap 在 Java 8 中的结构是怎么样的?
小红书
Agentic全栈研发练习生HashMap 是什么结构?
字节跳动
后端开发工程师Java 的hash表是什么? 这是怎么设计的?
阿里巴巴
AI 应用算法工程师HashMap原理
字节跳动、腾讯、蚂蚁集团
哈希表的了解
字节跳动
后端开发工程师(AI / Agent 方向)HashMap 底层是什么数据结构?
火齐境
HashMap底层数据结构
法大大
Java 开发工程师HashMap的数据结构
亚信科技
后端开发工程师(AI / Agent 方向)详细讲一下ArrayList和HashMap
网易
程序开发hashmap底层原理,是否线程安全
腾讯音乐
后端开发工程师HashMap 底层是怎么实现的?
公司未注明
后端开发工程师2讲一下hashmap底层原理
阿里巴巴
后端开发工程师(AI)HashMap的底层实现讲一下?1.7和1.8有什 么区别?
阿里巴巴
AI 应用开发工程师java掌握程度:hashmap集合里面有用到集合吗
快手
后端开发工程师HashMap底层数据结构
字节跳动
后端开发工程师(AI)介绍hashmap
美团
软件开发工程师(后端方向)讲讲 HashMap 的实现原理
用友金融
Java 后端开发实习生HashMap的数据结构?为什么使用红黑树?红黑树的原理?
虾皮
AI Infra 后端开发HashMap的实现原理
阿里巴巴
AI 应用开发HashMap 的底层实现原理如果往深了问,你会怎么回答
阿里巴巴
AI 应用研发HashMap底层实现原理,发生哈希冲突如何解决,多线程环境下会不会有问题
阿里巴巴
AI 应用研发HashMap底层实现原理
飞猪
Java 后端开发hashmap底层结构,怎么扩容,线性安全吗;concurrent hashmap线程安全,怎么实现的;1.8前后的区别
八斗科技
Java 开发实习生HashMap的底层,扩容以及1.7和1.8的区别
拼多多、蚂蚁集团
字节跳动
后端开发介绍hashmap
重庆森鑫炬智慧
Java 开发hashmap底层原理?jdk1.7、1.8?hashmap线程为什么不安全?
南讯软件
后端开发HashMap的底层和原理?
字节跳动
Agent 开发工程师2 hashmap实现,如何扩容












