HashMap 底层原理详解

1. 核心数据结构

JDK 1.7 及之前数组 + 链表
JDK 1.8 及之后数组 + 链表/红黑树(链表长度 ≥8 时转红黑树,≤6 时退化为链表)

// JDK 1.8 的 Node 定义(链表节点)
static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next; // 链表指针
}// 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;           // 颜色标识
}

2. 哈希函数设计

作用:将 Key 映射到数组索引,尽可能减少哈希冲突。
JDK 1.8 的优化

static final int hash(Object key) {int h;// 高16位与低16位异或,提升散列性return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

索引计算
index = (table.length - 1) & hash

  • 长度取模优化:哈希表容量为 2 的幂次时,(n-1) & hash 等效于 hash % n,但位运算更快。


3. put() 方法流程
  1. 计算哈希值:调用 hash(key)

  2. 初始化或扩容:若数组为空,调用 resize() 初始化(默认容量 16,负载因子 0.75)。

  3. 定位桶位置index = (n-1) & hash

  4. 处理哈希冲突

    • 链表插入

      • JDK 1.7:头插法(易导致死循环)。

      • JDK 1.8:尾插法(解决死循环问题)。

    • 树化处理:若链表长度 ≥8 且数组长度 ≥64,链表转红黑树。

  5. 覆盖或新增节点

    • Key 已存在:覆盖 Value,返回旧值。

    • Key 不存在:插入新节点,返回 null。

  6. 扩容检查:若元素总数 > 阈值(容量 × 负载因子),触发 resize()


4. 扩容机制(resize())

触发条件:元素数量超过阈值(容量 × 负载因子,默认 0.75)。
扩容流程

  1. 新容量计算:旧容量 × 2(保证容量始终为 2 的幂次)。

  2. 迁移元素

    • JDK 1.7:遍历旧数组,重新哈希每个元素到新数组(头插法)。

    • JDK 1.8:优化迁移逻辑,链表元素拆分为高位链和低位链(无需重新哈希):

      • 低位链:原索引位置

      • 高位链:原索引位置 + 旧容量

优化原理
由于新容量是旧容量的 2 倍,(newCap - 1) & hash 的结果仅取决于哈希值的第 log2(oldCap) 位是否为 1:

  • 若为 0 → 索引不变(低位链)。

  • 若为 1 → 索引 = 原索引 + 旧容量(高位链)。


5. 红黑树优化

树化条件

  • 链表长度 ≥ TREEIFY_THRESHOLD(默认 8)。

  • 数组长度 ≥ MIN_TREEIFY_CAPACITY(默认 64)。

退化条件

  • 红黑树节点数 ≤ UNTREEIFY_THRESHOLD(默认 6)。

优势

  • 链表查询复杂度 O(n),红黑树查询复杂度 O(logn),显著减少哈希冲突时的性能损耗。


6. 关键参数与默认值
参数默认值说明
DEFAULT_INITIAL_CAPACITY16默认初始容量
DEFAULT_LOAD_FACTOR0.75负载因子(扩容阈值 = 容量 × 负载因子)
TREEIFY_THRESHOLD8链表转红黑树的阈值
UNTREEIFY_THRESHOLD6红黑树退化为链表的阈值
MIN_TREEIFY_CAPACITY64允许树化的最小数组长度

7. 性能优化建议
  • 初始化容量:预估元素数量,避免频繁扩容(如预计存 1000 元素,初始容量设为 2048)。

  • 重写 hashCode() 和 equals():确保 Key 对象的哈希分布均匀且相等性判断准确。

  • 避免高频修改:多线程场景使用 ConcurrentHashMap


总结

  • 核心结构:数组 + 链表/红黑树,动态扩容优化性能。

  • 哈希设计:高位异或、位运算取模、红黑树优化冲突。

  • 线程安全:非线程安全,需使用替代方案。

  • 实战技巧:合理初始化容量、重写哈希方法、避免并发操作。


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

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

相关文章

使用MySQL时出现 Ignoring query to other database 错误

Ignoring query to other database 错误 当在远程连接软件中输入MySQL命令出现该错误 导致错误原因是&#xff1a;登录mysql时账户名没有加上u 如果出现该错误&#xff0c;退出mysql&#xff0c;重新输入正确格式进入即可&#xff01;

哈尔滨工业大学:大模型时代的具身智能

大家好&#xff0c;我是樱木。 机器人在工业领域&#xff0c;已经逐渐成熟。具身容易&#xff0c;智能难。 机器人-》智能机器人&#xff0c;需要自主能力&#xff0c;加上通用能力。 智能机器人-》人类&#xff0c;这个阶段就太有想象空间了。而最受关注的-类人机器人。 如何…

Javascript代码压缩混淆工具terser详解

原始的JavaScript代码在正式的服务器上,如果没有进行压缩,混淆,不仅加载速度比较慢,而且还存在安全和性能问题. 因此现在需要进行压缩,混淆处理. 处理方案简单描述一下: 1. 使用 terser 工具进行 安装 terser工具: # npm 安装 npm install terser --save-dev# 或使用 yarn 安…

Java String 常用方法详解

目录 一、获取字符串信息(一)获取字符串长度(二)获取指定索引处的字符(三)获取子字符串二、字符串比较(一)比较字符串内容(二)忽略大小写比较三、字符串转换(一)转换为大写(二)转换为小写四、字符串查找(一)查找子字符串的位置(二)从指定位置开始查找五、字符…

Linux驱动开发练习案例

1 开发目标 1.1 架构图 操作系统&#xff1a;基于Linux5.10.10源码和STM32MP157开发板&#xff0c;完成tf-a(FSBL)、u-boot(SSBL)、uImage、dtbs的裁剪&#xff1b; 驱动层&#xff1a;为每个外设配置DTS并且单独封装外设驱动模块。其中电压ADC测试&#xff0c;采用linux内核…

leetcode-代码随想录-哈希表-赎金信

题目 题目链接&#xff1a;383. 赎金信 - 力扣&#xff08;LeetCode&#xff09; 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 maga…

精品可编辑PPT | “新基建”在数字化智慧高速公路中的支撑应用方案智慧建筑智慧交通解决方案施工行业解决方案

本文详细阐述了“新基建”在数字化智慧高速公路中的支撑应用方案&#xff0c;从政策背景出发&#xff0c;指出国家在交通领域的一系列发展规划和指导意见&#xff0c;强调了智慧交通建设的重要性。分析了当前高速公路存在的问题&#xff0c;如基础感知设施不足、协同水平低、服…

C语言求3到100之间的素数

一、代码展示 二、运行结果 三、感悟思考 注意: 这个题思路他是一个试除法的一个思路 先进入一个for循环 遍历3到100之间的数字 第二个for循环则是 判断他不是素数 那么就直接退出 这里用break 是素数就打印出来 在第一个for循环内 第二个for循环外

英语—四级CET4考试—蒙猜篇—匹配题

蒙猜方法一 匹配题的做题&#xff1a; 方法一&#xff1a; 首先&#xff0c;什么都不想&#xff0c;把问题中ing形式的&#xff0c;大写字母的&#xff0c;人名&#xff0c;地名&#xff0c;最后几个依次框起来。 然后&#xff0c;比如46题&#xff0c;口里默念meaningful lif…

股票日数据使用_未复权日数据生成前复权日周月季年数据

目录 前置&#xff1a; 准备 代码&#xff1a;数据库交互部分 代码&#xff1a;生成前复权 日、周、月、季、年数据 前置&#xff1a; 1 未复权日数据获取&#xff0c;请查看 https://blog.csdn.net/m0_37967652/article/details/146435589 数据库使用PostgreSQL。更新日…

系统与网络安全------Windows系统安全(6)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 共享文件夹 发布共享文件夹 Windows共享概述 微软公司推出的网络文件/打印机服务系统 可以将一台主机的资源发布给其他主机共有 共享访问的优点 方便、快捷相比光盘 U盘不易受文件大小限制 可以实现访问…

BN 层的作用, 为什么有这个作用?

BN 层&#xff08;Batch Normalization&#xff09;——这是深度神经网络中非常重要的一环&#xff0c;它大大改善了网络的训练速度、稳定性和收敛效果。 &#x1f9e0; 一句话理解 BN 层的作用&#xff1a; Batch Normalization&#xff08;批归一化&#xff09;通过标准化每一…

判断HiveQL语句为ALTER TABLE语句的识别函数

写一个C#字符串解析程序代码&#xff0c;逻辑是从前到后一个一个读取字符&#xff0c;遇到匹配空格、Tab和换行符就继续读取下一个字符&#xff0c;遇到大写或小写的字符a&#xff0c;就读取后一个字符并匹配是否为大写或小写的字符l&#xff0c;以此类推&#xff0c;匹配任意字…

基于编程的运输设备管理系统设计(vue+springboot+ssm+mysql8.x)

基于编程的运输设备管理系统设计&#xff08;vuespringbootssmmysql8.x&#xff09; 运输设备信息管理系统是一个全面的设备管理平台&#xff0c;旨在优化设备管理流程&#xff0c;提高运输效率。系统提供登录入口&#xff0c;确保只有授权用户可以访问。个人中心让用户可以查…

6.1 python加载win32或者C#的dll的方法

python很方便的可以加载win32的方法以及C#编写的dll中的方法或者变量&#xff0c;大致过程如下。 一.python加载win32的方法&#xff0c;使用win32api 1.安装库win32api pip install win32api 2.加载所需的win32函数并且调用 import win32api win32api.MessageBox(0,"…

前端精度计算:Decimal.js 基本用法与详解

一、Decimal.js 简介 decimal.js 是一个用于任意精度算术运算的 JavaScript 库&#xff0c;它可以完美解决浮点数计算中的精度丢失问题。 官方API文档&#xff1a;Decimal.js 特性&#xff1a; 任意精度计算&#xff1a;支持大数、小数的高精度运算。 链式调用&#xff1a;…

SQL Server 数据库实验报告

​​​​​​​ 1.1 实验题目&#xff1a;索引和数据完整性的使用 1.2 实验目的&#xff1a; &#xff08;1&#xff09;掌握SQL Server的资源管理器界面应用&#xff1b; &#xff08;2&#xff09;掌握索引的使用&#xff1b; &#xff08;3&#xff09;掌握数据完整性的…

AI绘画中的LoRa是什么?

Lora是一个多义词&#xff0c;根据不同的上下文可以指代多种事物。以下将详细介绍几种主要的含义&#xff1a; LoRa技术 LoRa&#xff08;Long Range Radio&#xff09;是一种低功耗广域网&#xff08;LPWAN&#xff09;无线通信技术&#xff0c;以其远距离、低功耗和低成本的特…

哈希表(Hashtable)核心知识点详解

1. 基本概念 定义&#xff1a;通过键&#xff08;Key&#xff09;直接访问值&#xff08;Value&#xff09;的数据结构&#xff0c;基于哈希函数将键映射到存储位置。 核心操作&#xff1a; put(key, value)&#xff1a;插入键值对 get(key)&#xff1a;获取键对应的值 remo…

数据流和重定向

1、数据流 不管正确或错误的数据都是默认输出到屏幕上&#xff0c;所以屏幕是混乱的。所以就需要用数据流重定向将这两 条数据分开。数据流重定向可以将标准输出和标准错误输出分别传送到其他的文件或设备去 标准输入&#xff08;standard input&#xff0c;简称stdin&#xff…