当前位置: 首页 > news >正文

HashMap扩容过程是什么?怎么解决哈希冲突?

hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶

2025 面试题大全🔗


HashMap 如何解决哈希冲突?

哈希冲突是指两个或多个不同的键(key)通过哈希函数计算出了相同的数组下标。

HashMap 主要使用以下两种方法相结合来解决哈希冲突:

1. 链地址法

这是 HashMap 解决冲突的基本方法。

  • 原理:数组的每一个元素不是一个单独的值,而是一个"桶",可以存放多个元素。当发生哈希冲突时,将哈希值相同的键值对放入同一个桶中,并以链表的形式链接起来。
  • 过程
    1. 计算键的哈希值,找到对应的数组下标。
    2. 如果该位置为空,直接放入新节点。
    3. 如果该位置不为空(即发生冲突),则遍历这个链表:
      • 如果发现某个节点的 Key 与要插入的 Key 相同(通过 equals 方法判断),则更新其 Value。
      • 如果没有相同的 Key,则将新节点插入到链表的末尾(JDK 1.7是头插法,JDK 1.8及以后改为尾插法,主要是为了避免并发环境下扩容时产生死循环)。
2. 红黑树优化

在 JDK 8 之后,HashMap 对链地址法进行了重大优化,引入了红黑树。

  • 背景:当链表变得非常长时,在链表中查找一个元素的时间复杂度会退化为 O(n),效率很低。
  • 优化:当一个桶中的链表长度超过一定阈值(默认为 8并且 当前数组的长度达到一定阈值(默认为 64)时,链表会自动转换为红黑树
  • 目的:将查找、插入和删除操作的时间复杂度从 O(n) 优化到 O(log n),大大提升了性能。
  • 退化:同样,当红黑树中的节点数量由于删除操作而减少到另一个阈值(默认为 6)时,为了节省空间,红黑树会退化为链表。

总结解决哈希冲突的流程:
计算哈希 -> 定位桶 -> 若冲突,则在链表/红黑树中查找 -> 找到则更新,未找到则插入。


HashMap 的扩容过程

扩容(Rehashing)是 HashMap 保持高效性能的关键机制。当元素数量过多,导致哈希冲突概率急剧增加时,通过扩容来减少冲突。

1. 为什么要扩容?
  • 降低哈希冲突:数组长度越大,哈希值分布越分散,碰撞几率越低。
  • 维持高效性:如果链表过长或树化过多,会严重影响 HashMap 的性能。扩容可以有效减少每个桶中元素的数量。
2. 什么时候触发扩容?

HashMap 中的元素数量(size)超过 容量(capacity) 负载因子(loadFactor)* 时,就会触发扩容。

  • 容量:底层数组的长度,默认是 16
  • 负载因子:一个比例系数,默认是 0.75
  • 阈值threshold = capacity * loadFactor。默认情况下,threshold = 16 * 0.75 = 12

所以,当元素数量超过 12 时,就会触发第一次扩容

3. 扩容的具体步骤是什么?

扩容过程可以概括为:创建新数组 -> 重新哈希 -> 迁移数据

  1. 创建新数组

    • 创建一个新的数组,其容量是旧数组的 2 倍(即 newCapacity = oldCapacity << 1)。例如,从 16 扩容到 32。
  2. 重新哈希与数据迁移

    • 遍历旧数组中的每一个桶(bucket)。
    • 对于每个桶中的每一个节点(可能是链表节点,也可能是树节点):
      a. 计算新下标:根据节点的 Key 的哈希值和新数组的长度重新计算它在新数组中的下标。
      b. 关键优化:由于新容量是旧容量的 2 倍,所以每个节点在新数组中的位置要么保持不变,要么是 原位置 + 旧容量
      • 原因:计算下标的公式是 hash & (length - 1)。扩容后 length-1 的二进制比原来多了一个 1(例如从 01111 (15) 变成 11111 (31))。这个多出来的 1 位与哈希值进行 & 操作,结果只能是 0 或 1。如果是 0,则新下标等于原下标;如果是 1,则新下标等于 原下标 + 旧容量
        c. 放置到新数组
      • 对于链表:JDK 8 做了一个巧妙的优化。它会将整个链表拆分成两个子链表:一个放在新数组的原索引位置(loHead),另一个放在 原索引 + 旧容量 的位置(hiHead)。这样可以保证链表的相对顺序,并且避免了在并发环境下形成死循环(JDK 7 的头插法会有这个问题)。
      • 对于红黑树:当拆分一个树节点时,它同样会被拆分成两个链表。然后会检查拆分后的链表长度:
        • 如果长度 <= 6,则树退化为链表。
        • 如果长度 > 6,则将链表重新树化,形成一个新的红黑树。
  3. 更新引用

    • HashMap 内部的 table 引用指向新创建的数组。
    • 更新新的扩容阈值 threshold = newCapacity * loadFactor

要点

特性解决方案/过程
解决哈希冲突链地址法 为主,红黑树 优化长链表。
链表转树条件链表长度 > 8 数组长度 >= 64。
树退化为链表树节点数 <= 6。
扩容触发条件元素数量 > 容量 * 负载因子 (默认 16*0.75=12)。
扩容后大小原数组长度的 2 倍
扩容核心过程1. 创建2倍大小新数组。
2. 遍历旧数组,对每个节点重新计算下标(原位置原位置+旧容量)。
3. 将节点迁移到新数组。

2025 面试题大全🔗

在这里插入图片描述

http://www.dtcms.com/a/512124.html

相关文章:

  • OpenSSH 安全配置核心概念解析
  • TCL华星t8项目正式开工,总投资额约295亿元
  • 营销网站制作信ls15227微信网站建设公司首选
  • 新手指南:如何在悟空AI CRM中创建和管理客户
  • 网站建设來选宙斯站长网站建设运营合同范本
  • 新能源汽车的“隐形守护者”:深度解析车载充电机(OBC)的关键作用
  • AAIA:从 “普通审计” 到 “AI 专家” 的跃迁
  • 【系统分析师】核心考点:100个高频知识点汇总
  • 基于单片机的机房环境监测系统设计与实现
  • 做网站的每天打电话咋办深圳 微网站
  • 网站建设视屏电子商务网站开发工具
  • 2.常见软件测试分类的串联
  • Gemini CLI接入CloudBase-AI-Toolkit(MCP)保姆级教程
  • 阿里云代理商:阿里云CDN访问问题怎么诊断?
  • 关于yolov5 v2.0本地运行出现 的一些问题的解决
  • jsp做的网页是网站吗毕业设计拼车网站的建设雨实现
  • 不用ftp可以做网站吗html个人网页设计代码
  • AI 时代的数据通道:云消息队列 Kafka 的演进与实践
  • AWS云上Quickwit部署指南与成本分析
  • ziplist、quicklist、listpack之间的区别
  • 1.序列式容器-vectorlist
  • 长沙外贸建站用spl做网站
  • 无锡网站建设818gxwordpress 迁移 域名
  • 使用beautifulSoup提取信息
  • 一种独特机理驱动的化学反应分类器详解
  • 南京佛搜做网站公司wordpress支付宝支付
  • C++ 多态:面向对象编程中的灵活性与扩展性
  • 微信公众号内嵌网站开发做团购的的网站有哪些
  • 当前非英语国家中出现的“去英语化”趋势
  • CR后的反思、编辑表格实现