文章目录

前言

一、底层数据结构总览

二、核心组成部分详解

1. 数组(哈希表)

2. 节点(Node)

3. 红黑树(TreeNode)

三、哈希函数与索引计算

四、哈希冲突的解决

五、扩容机制

六、关键特性与注意事项

总结


前言

        在 Java 开发中,HashMap 是高频使用的集合类,从业务缓存到框架底层都离不开它,但多数开发者对其理解仅停留在 “键值对存储”,对 “为何高效”“为何踩坑” 的底层逻辑一知半解。

        面试中 “JDK1.7 与 1.8 的 HashMap 有何不同”“链表为何转红黑树”,开发中 “扩容导致性能波动”“哈希冲突引发查询变慢”,这些问题的答案,都藏在 HashMap 的底层设计里。它不是简单的 “数组 + 链表”,而是融合哈希算法、动态扩容、红黑树的复杂体系,每处细节都是 “时间与空间的权衡”。

        本文将拆解 HashMap 的核心设计:从结构演进(数组 + 链表→数组 + 链表 + 红黑树),到哈希冲突解决、扩容逻辑、红黑树转换,再到 key 的规范细节。无论你是新手还是资深工程师,都能理清设计逻辑,既应对面试考点,也能在开发中合理调优,写出更高效的代码。接下来,我们从 HashMap 的底层结构开始,逐层剖析。


HashMap 是 Java 集合框架中常用的实现类(实现 Map 接口),用于存储键值对(key-value)数据,其核心特点是查询、插入、删除效率高(平均时间复杂度为 O (1)),底层通过数组 + 链表 + 红黑树的复合数据结构实现,这种设计是为了平衡哈希冲突带来的性能问题。

一、底层数据结构总览

HashMap 在 JDK 1.8 中进行了重大优化,核心差异如下:

JDK 1.8 及之后,HashMap 的底层结构由三部分组成:

特性JDK 1.7JDK 1.8
底层结构数组 + 链表数组 + 链表 + 红黑树
链表插入方式头插法(新节点插入链表头部)尾插法(新节点插入链表尾部)
扩容时的 rehash需要重新计算所有节点的索引利用高位判断,减少计算量
冲突处理效率链表查询时间复杂度 O (n)红黑树查询时间复杂度 O (log n)
关键常量无红黑树相关阈值引入树化阈值(8)、链化阈值(6)
  • 数组(核心容器):称为「哈希表」或「桶(Bucket)」,是 HashMap 的主体,数组中的每个元素是一个「节点」(Node)。
  • 链表:当哈希冲突时,相同索引位置的节点会以链表形式存储。
  • 红黑树:当链表长度超过阈值(默认 8)且数组长度 ≥ 64 时,链表会转为红黑树,以优化查询效率(红黑树查询时间复杂度为 O (log n),优于链表的 O (n))。

结构示意图如下:

数组索引: 0   1   2        3        ...  n-1|   |   |        |v   v   v        v
节点:   Node Node Node -> Node -> ...  Node|vTreeNode (红黑树节点)

二、核心组成部分详解

1. 数组(哈希表)
  • 作用:作为底层容器,直接存储节点(Node),数组的长度称为「容量(Capacity)」。
  • 容量特性:默认初始容量为 16,且始终保持为 2 的幂次方(如 16、32、64...)。这是为了通过「与运算」高效计算索引(替代取模运算),同时保证哈希值分布更均匀。
  • 索引计算:数组索引由 key 的哈希值通过计算得到,公式简化为:index = (n - 1) & hash(n 为数组长度,hash 为 key 的哈希值经过扰动处理后的值)。
2. 节点(Node)

数组中的元素是 Node 对象,实现 Map.Entry 接口,包含四个核心字段:

static class Node<K,V> implements Map.Entry<K,V> {final int hash;    // key 的哈希值(经过扰动处理)final K key;       // 键(不可变)V value;           // 值(可变)Node<K,V> next;    // 下一个节点的引用(用于链表)
}
  • hash:用于定位节点在数组中的索引。
  • next:当发生哈希冲突时,通过该字段形成链表(指向同索引下的下一个节点)。
3. 红黑树(TreeNode)

当链表长度超过阈值(默认 8)且数组长度 ≥ 64 时,链表会转为红黑树(TreeNode 继承 Node),以优化查询性能。TreeNode 额外包含红黑树的核心字段:

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {TreeNode<K,V> parent;  // 父节点TreeNode<K,V> left;    // 左子节点TreeNode<K,V> right;   // 右子节点TreeNode<K,V> prev;    // 前一个节点(用于回退为链表)boolean red;           // 节点颜色(红/黑)
}
  • 红黑树是一种自平衡二叉搜索树,通过保持「黑色平衡」确保树的高度稳定(约为 log n),避免链表过长导致的查询效率下降。
  • 当红黑树节点数减少到 6 时,会重新转为链表(平衡查询和插入效率)。

三、哈希函数与索引计算

HashMap 通过哈希函数将 key 映射到数组索引,核心目的是让 key 均匀分布在数组中,减少哈希冲突。步骤如下:

  1. 计算 key 的原始哈希值:调用 key.hashCode(),得到一个 32 位整数(不同 key 可能返回相同值,即「哈希碰撞」)。

  2. 扰动处理(哈希值优化):对原始哈希值进行二次处理,让高位参与运算,减少碰撞概率。
    JDK 1.8 中的实现:

    static final int hash(Object key) {int h;// 若 key 为 null,哈希值为 0;否则,将 hashCode 的高 16 位与低 16 位异或return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
     

    作用:将高位信息融入低位,避免因数组长度较小时,高位无法参与索引计算导致的分布不均。

  3. 计算数组索引:用处理后的哈希值与「数组长度 - 1」进行「与运算」:

    index = (n - 1) & hash;  // n 为数组容量(2的幂次方)
    
     

    由于 n 是 2 的幂次方,n - 1 的二进制为「全 1」(如 15 是 1111),与运算等价于「取模运算」,但效率更高。

四、哈希冲突的解决

哈希冲突指不同 key 经过计算得到相同的数组索引。HashMap 采用链地址法(拉链法) 解决:

  • 当冲突发生时,新节点会被添加到该索引位置的链表尾部(JDK 1.8 后)。
  • 若链表长度超过阈值(默认 8)且数组长度 ≥ 64,链表转为红黑树;若数组长度 < 64,则先触发扩容(而非转树),避免数组较小时频繁树化。

五、扩容机制

当 HashMap 中的元素数量(size)超过「阈值(threshold)」时,会触发扩容(resize),以减少哈希冲突。

  1. 阈值计算threshold = 容量(n) × 负载因子(loadFactor)

    • 负载因子默认 0.75(JDK 设计的平衡值:既避免空间浪费,又减少冲突)。
    • 例:初始容量 16,阈值 = 16 × 0.75 = 12,当元素数 > 12 时触发扩容。
  2. 扩容过程

    • 新容量 = 原容量 × 2(始终保持 2 的幂次方)。
    • 重新计算阈值(新容量 × 负载因子)。
    • 重建哈希表:将原数组中的所有节点重新计算索引,迁移到新数组中(此过程称为 rehash)。
    • JDK 1.8 优化:rehash 时,节点新索引要么是原索引,要么是原索引 + 原容量(无需重新计算哈希值,仅通过判断高位即可),减少计算开销。

六、关键特性与注意事项

  1. 无序性:节点存储位置由哈希值决定,遍历顺序与插入顺序无关(如需有序,可使用 LinkedHashMap)。
  2. 线程不安全:多线程环境下可能出现死循环(扩容时)或数据不一致,需使用 ConcurrentHashMap 替代。
  3. key 特性
    • 允许 key 为 null(仅允许一个,索引固定为 0)。
    • key 需重写 hashCode() 和 equals() 方法(否则可能导致无法正确查询、删除元素)。
  4. 性能影响因素:容量、负载因子、哈希函数的优劣直接影响冲突率,进而影响性能。

七、key 的 hashCode () 和 equals () 规范

HashMap 对 key 的两个方法有严格要求,否则会导致数据异常(如无法查询到已插入的元素):

  1. equals () 相等的对象,hashCode () 必须相等
    若 a.equals(b) == true,则 a.hashCode() == b.hashCode() 必须成立。否则会导致两个相等的 key 被映射到不同索引,无法正确查询。

  2. hashCode () 相等的对象,equals () 可以不相等
    这会导致哈希冲突,此时通过 equals () 区分节点(遍历链表 / 红黑树时,需用 equals () 比较 key 是否真正相等)。

  3. 最佳实践
    重写 hashCode () 时,应结合对象的关键属性,且保证:

    • 属性不变时,hashCode () 返回值不变。
    • 尽量让不同对象的 hashCode () 分布均匀(减少冲突)。

总结

        HashMap 是「数组 + 链表 + 红黑树」的复合结构,通过哈希函数定位元素,链地址法解决冲突,扩容机制平衡空间与性能,最终实现高效的 key-value 存储与访问。其设计体现了时间复杂度与空间复杂度的权衡,是 Java 中最常用的集合类之一。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/922349.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/922349.shtml
英文地址,请注明出处:http://en.pswp.cn/news/922349.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于电脑连接不到5g的WiFi时的一些解决办法

方法一、设备管理器重卸载驱动后&#xff0c;重装驱动。方法二、打开控制面板 “控制面板\网络和 Internet\网络连接” &#xff08;亲测有效&#xff09;点击更改适配器配置右击当前的WLAN属性点击配置选择“高级” 802.11a/b/g 无线模式选项栏 值&#xff1a;6.的双…

Mathtype公式批量编号一键设置公式居中编号右对齐

插件[ygtools] 批量编号一键设置公式居中编号右对齐 单栏/多栏均可https://wwon.lanzout.com/i0NRf35vyw8j 下载密码8543

基于ssm的小橘子出行客户体验评价系统[SSM]-计算机毕业设计源码+LW文档

摘要&#xff1a;随着出行行业的快速发展&#xff0c;客户体验评价对于出行服务质量的提升至关重要。本文设计并实现了基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的小橘子出行客户体验评价系统。该系统涵盖系统用户管理、司机信息管理、客户评价管理等功…

算法日记---二分查找

目录 前言 一、二分查找 1.思想 2.简单二分 3.优点 4.局限性 二、模板 1.基本模板 2.简单例题&#xff08;LeetCode&#xff09; 4.有重复元素的二分 5.0-1问题 总结 前言 本文通过讲解简单的二分查找配合leetcode例题对二分查找本质、模板进行了基础的总结 提示&a…

Level Set(水平集)算法——形象化讲解

目录 维度一&#xff1a;核心思想与比喻&#xff08;它像什么&#xff1f;&#xff09; 维度二&#xff1a;要解决什么问题&#xff1f;&#xff08;它能干嘛&#xff1f;有什么用&#xff1f;&#xff09; 维度三&#xff1a;工作原理&#xff08;它是怎么做到的&#xff1…

DDoS 攻防“军备竞赛”的幕后

谈到 DDoS&#xff08;分布式拒绝服务攻击&#xff09;&#xff0c;很多人会想到“黑客租用肉鸡发流量&#xff0c;网站直接崩”。但事实上&#xff0c;如今的 DDoS 攻防早已变成一场 军备竞赛。攻击者的武器越来越“工业化”&#xff1a;僵尸网络商品化&#xff1a;黑市上&…

如何用 Rust 重写 SQLite 数据库(二):是否有市场空间?

用 Rust 实现一个类似 SQLite 的嵌入式数据库非常有意义&#xff0c;但需要结合具体目标和场景来评估其价值。以下从技术、生态、市场需求和个人成长等多个维度展开分析&#xff0c;并给出结论。一、技术价值&#xff1a;Rust 与数据库的天然契合 SQLite 作为全球装机量最大的数…

【Web】ImaginaryCTF 2025 wp

目录 imaginary-notes certificate codenames-1 passwordless pearl imaginary-notes I made a new note taking app using Supabase! Its so secure, I put my flag as the password to the "admin" account. I even put my anonymous key somewhere in the si…

oracel如何找到外键子表

要找到导致外键约束冲突的子表&#xff08;即包含"child record"的表&#xff09;&#xff0c;可以通过以下SQL查询在Oracle数据库中定位&#xff1a;1. 查询约束基本信息&#xff08;确定父表和子表&#xff09;SELECT owner, constraint_name, table_name AS child…

智源研究院新研究:突破物理世界智能边界的RoboBrain 2.0,将重构具身AI能力天花板

当你对着家用机器人说"把杯子放在笔筒和键盘之间&#xff0c;对齐杯身logo"时&#xff0c;它能精准理解空间关系并执行动作&#xff1b;当多台机器人在超市协作补货时&#xff0c;它们能自主规划轨迹、避免冲突并完成长周期任务——这些曾经出现在科幻电影中的场景&a…

【2025】Office核心组件Microsoft word,Excel,PowerPoint详细使用指南

Office 核心组件使用指南 Microsoft Word 文字处理 Word主要用于创建和编辑文档&#xff0c;如信件、报告、论文等。 2025Office&#x1f517; 1. 界面认识 快速访问工具栏&#xff1a;位于左上角&#xff0c;可自定义保存、撤销、恢复等常用命令。功能区&#xff1a;顶部…

【模型训练篇】VeRL的使用 - RL(PPO)与源码

继续学习字节家的VeRL&#xff0c;今天来看看VeRL的RL&#xff0c;是VeRL系列的第三篇文章&#xff08;话说近期好多大事儿&#xff0c;我司发布了Longcat、韩立结婴、阿里周五发布了QWen-Next都是好东西啊&#xff0c;学不过来了damn&#xff09; 底层分布式能力基础Ray&…

QML Charts组件之折线图的鼠标交互

目录前言相关系列代码示例详解&#xff08;LineSeriesDemo3.qml&#xff09;功能概览运行效果代码说明工程下载参考前言 接上文&#xff08;QML Charts组件之折线图的基础属性&#xff09;&#xff0c;本文将重点介绍LineSeries的鼠标交互&#xff0c;包括&#xff1a;鼠标拖拽…

二值信号量——学习笔记12

本文是笔者在学习 正点原子官方 的《【正点原子】手把手教你学FreeRTOS实时系统》系列视频时整理的笔记。 视频讲解清晰透彻&#xff0c;非常感谢UP主的无私奉献&#xff01;原课程链接如下&#xff1a; &#x1f449; B站视频链接&#xff1a;​​​​​​【正点原子】手把手教…

裸机开发 时钟配置,EPIT

1.概念时钟(clock)&#xff1a;在电子系统中是一个产生稳定、周期性振荡信号的电路或组件。这个信号像节拍器或心跳一样&#xff0c;为数字电路中的各种操作提供同步时序基准。PLL&#xff08;phase locked loop&#xff09;锁相环电路: 倍频PFD&#xff08;phase fractional P…

Linux-文本三剑客(grep、sed、awk)

Linux-文本三剑客前言一、grep二、sed三、awk模式 -- 正则表达式关系表达式、运算符表达模式匹配表达式动作 输出流程控制参数传递&#xff0c;awk接受外部变量统计数组的使用分组统计练习常用内置函数前言 grep、sed、awk 被称为 “文本三剑客”&#xff0c;它们是处理文本文…

主流反爬虫、反作弊防护与风控对抗手段

文章目录1. 写在前面2. 指纹检测3. 行为验证3. 加固防护4. 链路检测5. 风控埋点6. 游客注册7. 数据防护8. 账号权重9. 反调阻断【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、…

金蝶云星空插件开发记录(一)

实现目的&#xff1a;新增供应商保存后&#xff0c;触发钉钉审批流程&#xff0c;并根据钉钉审批结果回写是否合格供应商。实现思路&#xff1a;通过BOS平台供在应商管理界面新增两个复选框字段&#xff1a;是否钉钉审批、是否合格供应商&#xff0c;若在新建供应商档案时勾选是…

企业跨区域组网新解:SD-WAN技术打造安全稳定网络体系

前言在数字化浪潮席卷全球的今天&#xff0c;企业跨区域网络互联已成为支撑业务发展的关键基础设施。传统MPLS专线虽性能稳定&#xff0c;但高昂成本和漫长部署周期令众多企业望而却步。SD-WAN技术的出现&#xff0c;正以其智能、灵活和成本效益的优势&#xff0c;重塑企业组网…

Docker 容器化

引言在解释docker是什么之前&#xff0c;我们首先应该先了解的是容器化的概念。什么是容器&#xff1f;就是一个沙箱&#xff0c;在这个沙箱中涵盖了特定应用运行的一切依赖的内容。但他不是一个操作系统&#xff0c;且和底层的操作系统是隔离的。什么是容器化&#xff1f;容器…