news 2026/4/23 1:46:33

Java8 为什么这里把key的hashcode取出来,然后把它右移16位,然后取异或?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java8 为什么这里把key的hashcode取出来,然后把它右移16位,然后取异或?

文章目录

  • 【深入源码】图解 HashMap 扰动函数:为什么要把高位“揉”进低位?
    • 1. 核心矛盾:被浪费的“40亿”
    • 2. 案例实战:如果不“扰动”会发生什么?
      • 未经扰动的下标计算:
    • 3. 扰动函数介入:h ^ (h >>> 16)
      • 演示 A 的变换过程:
      • 演示 B 的变换过程:
    • 4. 最终对比:碰撞消失了!
  • 思维误区:其实原来哈希冲突的原因就是因为低位雷同,现在h&(h>>16)就是保证高16和低16位都是原值的高位信息,导致你h&(n-1)就是用不一样的高位去取模计算索引位置了?
      • 1. 修正一个小偏差:是“混合”而非“覆盖”
      • 2. 为什么能减少冲突?(逻辑闭环)
      • 3. 一个直观的对比

【深入源码】图解 HashMap 扰动函数:为什么要把高位“揉”进低位?

在阅读 HashMap 源码时,很多小伙伴会被(h = key.hashCode()) ^ (h >>> 16)这一行代码困惑。为什么要右移 16 位?为什么要进行异或?本文通过一个具体的案例,带你像剥洋葱一样看透这个“扰动函数”的奥秘。


1. 核心矛盾:被浪费的“40亿”

hashCode是一个 32 位的整数,范围高达 40 亿。但现实中,我们的初始数组长度往往只有 16。

在计算下标时,公式为:(n - 1) & hash
如果数组长度为 16,计算过程只取决于最后 4 位。这意味着,即便高位有再大的差异,只要低 4 位相同,就一定会发生哈希碰撞。


2. 案例实战:如果不“扰动”会发生什么?

假设我们有两个哈希值h A h_AhAh B h_BhB,它们的高位差异极大,但低位完全一模一样:

  • h A h_AhA:1111 0000 0000 0000 | 0000 0000 0000 0101
  • h B h_BhB:0101 0101 0101 0101 | 0000 0000 0000 0101

未经扰动的下标计算:

n = 16时,(16 - 1)的二进制是1111

  • A 的下标:...0101 & 1111 = 5
  • B 的下标:...0101 & 1111 = 5
  • 结果:发生严重碰撞!(高位的差异被完全忽略了)

3. 扰动函数介入:h ^ (h >>> 16)

扰动函数的目的就是:让高 16 位的特征“掉下来”,混合到低 16 位中。

演示 A 的变换过程:

  1. 原值h A h_AhA:1111 0000 0000 0000 | 0000 0000 0000 0101
  2. 右移 16 位:0000 0000 0000 0000 | 1111 0000 0000 0000
  3. 异或运算:
1111 0000 0000 0000 | 0000 0000 0000 0101 (原值) ^ 0000 0000 0000 0000 | 1111 0000 0000 0000 (移位值) ------------------------------------------- 1111 0000 0000 0000 | 1111 0000 0000 0101 (扰动结果)

最终 A 的低 4 位仍为0101(十进制 5)。

演示 B 的变换过程:

  1. 原值h B h_BhB:0101 0101 0101 0101 | 0000 0000 0000 0101
  2. 右移 16 位:0000 0000 0000 0000 | 0101 0101 0101 0101
  3. 异或运算:
0101 0101 0101 0101 | 0000 0000 0000 0101 (原值) ^ 0000 0000 0000 0000 | 0101 0101 0101 0101 (移位值) ------------------------------------------- 0101 0101 0101 0101 | 0101 0101 0101 0000 (扰动结果)

最终 B 的低 4 位变为了0000(十进制 0)。


4. 最终对比:碰撞消失了!

处理方式A 的下标B 的下标结果
直接取低位55碰撞!
扰动后再取50分流成功!

深度总结:
原本 A 和 B 的低 4 位完全相同。但通过右移 16 位,A 的高位特征1111和 B 的高位特征0101分别“揉”进了自己的低位。

即使你的数组很小,这种机制也强制让 32 位信息中的每一位都参与了最终的下标运算。这就是利用“40亿”种可能性来减少碰撞的精髓所在。


思维误区:其实原来哈希冲突的原因就是因为低位雷同,现在h&(h>>16)就是保证高16和低16位都是原值的高位信息,导致你h&(n-1)就是用不一样的高位去取模计算索引位置了?

你已经抓住了核心逻辑,但有一个小细节需要修正:不是“都是高位信息”,而是“融合了高低位的信息”

你的理解中有一个很棒的洞察:“用不一样的高位去计算索引位置”。没错,就是通过“借位”的思想,让本来不参与运算的高位,变相地参与了运算。

为了确保你面试时描述得绝对精准,我们把这个逻辑最后梳理一下:

1. 修正一个小偏差:是“混合”而非“覆盖”

你提到的h ^ (h >>> 16)

  • 高 16 位:因为右移后高位补 0,h ^ 0还是原值。所以高 16 位保持不变
  • 低 16 位:变成了原低16位 ^ 原高16位

所以,并不是说低位变成了纯高位信息,而是低位现在承载了全 32 位的“综合特征”

2. 为什么能减少冲突?(逻辑闭环)

  1. 原本的死穴n-1(比如 15)像是一个只看身份证最后 4 位的保安。只要最后 4 位一样,他就觉得是同一个人。
  2. 现在的解决办法:在过保安岗之前,我们先做一个动作——把身份证的前 16 位和后 16 位做一次异或。
  3. 结果:即使两个人的身份证后 4 位原本一样,但只要前 16 位有任何不同,异或后的“新后 4 位”大概率就不一样了。

3. 一个直观的对比

假设数组长度为 16(即& 1111):

  • 没有扰动时

    • Key1:0000...0001&1111=1
    • Key2:1111...0001&1111=1冲突!虽然 Key2 高位全是 1,但被保安无视了)
  • 有了扰动后

    • Key1: 低位还是接近0001
    • Key2: 低位变成了0001 ^ 1111=1110
    • Key2计算索引:1110&1111=14
    • 冲突解除!高位的1111成功自救,把 Key2 送到了 14 号位置。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 1:46:18

Claude Code Hooks 实战:用钩子打造自动化工作流

Claude Code 的 Hooks 系统允许你在特定事件发生时自动执行脚本。比如在写入文件后自动格式化、在执行命令前做安全检查、在会话结束时发送通知。本文通过 7 个完整的实战案例,带你掌握 Hooks 的高级用法。 一、Hooks 基础概念 1.1 四种 Hook 类型 Hook 类型触发…

作者头像 李华
网站建设 2026/4/23 1:46:18

WebSocket 全面解析:让浏览器“实时说话”的黑科技(建议收藏)

智能体时代的代码范式转移与 C# 的战略转型 传统的 C# 开发模式,即所谓的“工程导向型”开发,要求开发者创建一个复杂的项目结构,包括项目文件(.csproj)、解决方案文件(.sln)、属性设置以及依赖…

作者头像 李华
网站建设 2026/4/23 1:38:58

RAG系统中LLM微调策略与实践指南

1. RAG系统中的LLM微调概述在构建检索增强生成(RAG)系统时,我们通常将注意力集中在检索组件上——如何优化向量数据库、改进索引策略、管理上下文长度等。但作为RAG系统的另一核心组件,生成模型(LLM)的适配…

作者头像 李华
网站建设 2026/4/23 1:29:48

Honey Select 2 进阶体验:从基础API到画质优化的必备插件指南

1. 基础框架搭建:插件系统的核心组件 当你第一次打开Honey Select 2的mod文件夹时,可能会被各种.dll文件和压缩包搞得晕头转向。别担心,我们先从最基础的框架开始搭建。就像盖房子需要打地基一样,这些核心组件是所有高级功能的前提…

作者头像 李华
网站建设 2026/4/23 1:28:36

别再乱加磁珠了!一个真实案例带你复盘开关电源自激振荡的排查与修复

磁珠引发的电源灾难:一次自激振荡故障的完整解剖实录 示波器屏幕上那条疯狂跳动的波形线,让整个实验室的空气瞬间凝固。电源工程师老张盯着CH3通道上高达200mV的峰峰值纹波,手指不自觉地敲打着工作台面。这个采用同步整流架构的12V转5V电源模…

作者头像 李华