HashMap的奇幻漂流:当一个数组决定去整容

embedded/2025/3/15 6:22:12/

标准答案(面试官最爱版)

HashMap实现原理

  1. 数据结构:数组+链表/红黑树(Java 8+)
  2. 算法>哈希算法(h = key.hashCode()) ^ (h >>> 16)
  3. 索引计算(n-1) & hash(n为数组长度)
  4. 冲突解决:链表→红黑树(阈值=8),树→链表(阈值=6)
  5. 扩容机制:2倍扩容,负载因子默认0.75

用程序员黑话:

“它就是个会变形的瑞士卷——平时是夹心饼干(数组+链表),吃撑了变千层蛋糕(红黑树)”


一、初入江湖:数组的容貌焦虑

1.1 基础人设:图书馆管理员

假设你有个16层的书架(数组),每层放着:

  • 空书架:等待书籍(Entry)入驻
  • 单本书:直接上架(没有哈希冲突)
  • 一摞书:链表结构(哈希碰撞时叠罗汉)
  • 精装书柜:红黑树形态(当书堆超过8本)
// 典型书架结构
[null,Node<K,V>,        // 单本书TreeNode<K,V>,    // 精装书柜LinkedList<Node> // 书堆
]

1.2 哈希函数:图书编号玄学

图书上架流程:

public int hashCode() {return 作者姓氏首字母.charCodeAt(0) * 31+ 书名首字母.charCodeAt(0); // 31是玄学质数
}

现实往往是:

  • 《百年孤独》和《白夜行》可能挤在同一层(哈希碰撞)
  • 管理员(HashMap)碎碎念:“都怪马尔克斯和东野圭吾姓氏首字母都是B!”

二、冲突解决:书架的生存战争

2.1 链表模式:叠罗汉的艺术

当新书到来时:

  1. 先按哈希值找楼层
  2. 如果该层已有书:
    • Java 7:新书放最上面(头插法)
    • Java 8:新书放最下面(尾插法)

为什么改尾插法?

答:为了防止多线程扩容时形成环形链表(曾经导致CPU 100%的惨案)

2.2 红黑树模式:卷王诞生

当某层书堆超过8本:

if (binCount >= TREEIFY_THRESHOLD - 1) {treeifyBin(tab, hash); // 呼叫变形金刚!
}

变形代价:

  1. 需要书架总层数≥64(MIN_TREEIFY_CAPACITY)
  2. 消耗CPU给书堆做美甲(树化操作)

降级机制:当书减少到6本时,变回普通书堆(防止频繁转换)


三、扩容风暴:图书馆扩建计划

3.1 触发条件:书架负载率超75%

if (size > threshold) {resize(); // 警报!需要扩建!
}

扩建流程:

  1. 新书架扩容2倍(16→32层)
  2. 旧书籍重新计算楼层:
    • 原楼层 或 原楼层+旧容量(二进制魔术)
    • 例如旧楼层=5(0101),新楼层=5或5+16=21

3.2 死亡遍历:多线程的噩梦

未同步的HashMap在扩容时:

  • 线程A正在搬书到新书架
  • 线程B突然来借书
  • 结果可能:拿到null、死循环、数据丢失

解决方案

用ConcurrentHashMap——给每个书架配保安(分段锁)


四、使用禁忌:程序员的防秃指南

4.1 自定义对象的坑

class Student {String name;// 忘记重写hashCode和equals...
}// 使用后果:
map.put(new Student("张三"), 90);
map.get(new Student("张三")); // 返回null(当场秃头)

4.2 负载因子调参玄学

  • 设0.9:节省内存但容易碰撞(书架挤成沙丁鱼罐头)
  • 设0.5:查询快但内存翻倍(每个书架只放半本书)

黄金建议:非必要别动默认0.75,这是数学家算出来的魔法数字


五、灵魂拷问:为什么不用二叉搜索树?

  1. 树化成本高:链表变树要旋转染色(美甲时间)
  2. 退化合订本:树节点占用内存是链表的2倍
  3. 小数据优势:链表在少量数据时遍历更快

用生活比喻:

整理5本书用书堆(链表)更快,整理50本书才需要书柜(红黑树)

请添加图片描述


http://www.ppmy.cn/embedded/172686.html

相关文章

YOLOv8模型改进 第三十二讲 添加Transformer Self Attention TSA 解决CNN过程中特征丢失的问题

在医学图像分割过程中&#xff0c;卷积操作的局部性导致全局信息缺失&#xff0c;连续下采样导致细节丢失&#xff0c;以及跳跃连接未能有效融合多尺度特征。TSA通过自注意力机制捕捉全局上下文&#xff0c;结合位置编码保留空间信息&#xff0c;同时多头机制增强特征表达能力。…

[RA-L 2023] Coco-LIC:基于非均匀 B 样条的连续时间紧密耦合 LiDAR-惯性-相机里程计

这段代码是一个基于 C 的均匀 B 样条&#xff08;Uniform B-spline&#xff09;实现&#xff0c;专门用于表示 SE(3) 变换&#xff08;即三维空间中的刚体变换&#xff0c;包括旋转和平移&#xff09;。以下是对代码的总结&#xff1a; 1. 许可证和版权 使用 BSD 3-Clause Li…

养生,点亮健康生活

在当今快节奏的社会&#xff0c;人们常常在忙碌奔波中&#xff0c;忽略了健康才是生活的根本。养生&#xff0c;并非是老年人的专属&#xff0c;而是关乎每一个渴望生活品质、追求健康体魄之人的终身课题。它就像一位无声的守护者&#xff0c;默默改善着我们的身体机能&#xf…

【Javascript网页设计】个人简历网页案例

代码如下: <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>个人简历 - 张三…

爬虫案例八js逆向爬取网易音乐

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、js逆向的前期准备二、网站分析三、代码 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬取网易音乐 提示&#xff1a;以下是本篇…

【C++指南】string(二):深入探究 C++ `basic_string`:成员变量、函数全解析

. &#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 文章目录 引言basic_string 的成员变量内部结构概述示例代码推测成员变量 默认成员函数构造函数析构函…

maven--依赖的搜索顺序

原文网址&#xff1a;maven--依赖的搜索顺序-CSDN博客 简介 本文介绍maven中依赖的搜索顺序。 依赖搜索顺序 maven项目使用的仓库的方式 中央仓库。 这是默认的仓库。对应url为&#xff1a;http://repo1.maven.org/maven2/镜像仓库。 通过 settings…

jvm汇总

JDK、JRE、JVM、Java的区别 JVM是Java虚拟机&#xff0c;JRE是Java运行环境&#xff0c;JDK是个Java开发的工具包&#xff0c;Java是门编程语言。 JVM&#xff08;Java Virtual Machine&#xff09;&#xff1a;是Java虚拟机&#xff0c;是Java程序运行的基础&#xff0c;它将…