讲师中心 微信公众号
AI工具推荐 视频效率加速

在Java中HashMap的基本工作流程_Java哈希表原理解析

风丽吖_1211

风丽吖_1211

发布时间:2026-02-25 12:52:54

|

649人浏览过

|

来源于php中文网

原创

hash()扰动函数不可省,因其将高16位异或进低16位,避免低位规律性导致哈希分布不均;若省略,自增主键等场景下桶分布恶化5–8倍,O(1)退化为O(n)。

在java中hashmap的基本工作流程_java哈希表原理解析

hash() 扰动函数为什么不能省?

直接用 key.hashCode() 做索引计算,极容易在低比特位规律性强的场景下崩——比如数据库主键自增(hashCode 就是 1,2,3…)、时间戳毫秒值、或某些 ORM 生成的 ID,它们的低位变化频繁但高位几乎不变。这时未扰动的 hash 值 & (n-1) 实际只用了低几位,导致大量 key 挤在前几个桶里,链表暴涨,O(1) 变成 O(n)。

Java 8 的 hash() 强制把高16位异或进低16位:(h = key.hashCode()) ^ (h >>> 16),让高位也参与桶定位。实测显示,对连续整数 key,扰动后桶分布均匀度提升 5–8 倍。

  • 自定义 key 类必须重写 hashCode(),否则默认 Object.hashCode() 返回内存地址,不同实例几乎必冲突
  • 如果 key 是不可变类(如 String、Integer),不用管;但若用可变对象作 key(如没设 final 的 POJO),后续修改字段会导致 hashCode() 变,get() 就再也找不到它了

put() 时桶位置怎么算?别再用 % 取模

HashMap 不用 hash % table.length,而是用 (table.length - 1) & hash。前提是数组长度必须是 2 的幂(16、32、64…),这样 table.length - 1 就是形如 0b1111 的掩码,位与运算比取模快一个数量级,且无除法开销。

例如容量 16 → 掩码 15(0b1111),hash = 12345(0b11000000111001),12345 & 15 = 9,直接落到索引 9 —— 这就是真实桶下标。

立即学习“Java免费学习笔记(深入)”;

Java Api Extractor
Java Api Extractor

从 Java 项目中提取 Controller 层接口定义为 JSON 数据。

下载
  • 扩容时新容量翻倍(如 16→32),掩码多一位(0b11111),旧桶中每个节点只需看新增那一位是 0 还是 1,就能决定留在原位还是移到 oldCap + index,无需重新调用 hash()
  • 如果你手动指定初始容量,别传奇数或质数(如 17、100),要传 2 的幂(new HashMap(32)),否则构造器会自动向上取到最近的 2 的幂(100 → 128)

链表什么时候转红黑树?两个条件缺一不可

不是链表一长到 8 就树化。必须同时满足:table.length >= 64 且 链表节点数 >= 8。前者是为了避免小容量下树结构带来的额外内存和维护成本(TreeNode 比 Node 大得多),后者才是触发阈值。

反向退化更宽松:只要红黑树节点数 ≤ 6,就立刻转回链表,不检查容量。

  • 如果你的应用 key 冲突严重(比如用用户手机号做 key,但部分号段集中),建议初始化时设大点容量(如 new HashMap(1024)),提前规避链表过长
  • 树化后 get() 查找是 O(log n),但插入/删除代价更高;高频写、低频读场景慎用树化,可考虑换哈希算法或预分片
  • 用 jdk.internal.vm.annotation.Contended 等方式缓存行对齐优化,对极端并发场景有帮助,但属于高级技巧,普通业务无需介入

get() 返回 null 到底是没找到,还是 value 就是 null?

两者都可能。HashMap 允许 value 为 null,也允许一个 null key,所以 map.get("xxx") == null 完全无法判断 key 是否存在。

正确姿势是:查存在性用 map.containsKey(key),查 value 用 map.get(key),二者语义不同,不可混用。

  • null key 固定落在 table[0](因为 hash(null) == 0),但前提是 table 已初始化;首次 get(null) 会触发延迟初始化,这点常被忽略
  • 多线程环境下,即使加了同步,containsKey() 和 get() 之间仍可能被其他线程修改,如需强一致性,得用 ConcurrentHashMap 或外部锁
  • 如果业务逻辑里 value 绝对不允许为 null,建议用 Optional 包装,或统一约定用特殊哨兵值(如 -1、""),从源头堵住歧义

真正容易被忽略的是:扰动函数虽小,却是整个哈希分布的基石;而 containsKey() 和 get() 的语义差异,在复杂条件分支里一旦写错,debug 成本远高于预防成本。

热门AI工具

更多
切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

7079

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

length函数用法
length函数用法

length函数用于返回指定字符串的字符数或字节数。可以用于计算字符串的长度,以便在查询和处理字符串数据时进行操作和判断。 需要注意的是length函数计算的是字符串的字符数,而不是字节数。对于多字节字符集,一个字符可能由多个字节组成。因此,length函数在计算字符串长度时会将多字节字符作为一个字符来计算。更多关于length函数的用法,大家可以阅读本专题下面的文章。

1947

2023.09.19

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4618

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

1289

2025.12.24

Docker Desktop怎么设置端口映射
Docker Desktop怎么设置端口映射

本专题聚焦Docker Desktop容器端口映射与访问配置,整理从创建容器、设置端口绑定到验证本地服务访问的操作流程。针对端口被占用、映射配置错误、容器服务无法访问等情况,也提供相应的检查方向,方便用户完成端口配置。

0

2026.11.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn