深入理解 HashMap 的索引计算:右移与异或的作用

在 Java 中,HashMap 是一种高效的数据结构,它通过将键映射到数组中的索引位置来实现快速的插入和查找。但之前看源码总是理解到它要hash之后散列到数组中某一个位置,但却从未深究它究竟怎么散列的,如果不够散那就意味着hash冲突增加,在这个过程中,散列函数的设计至关重要,特别是右移和异或操作如何帮助减少哈希冲突。

  • 冲突对性能的影响
    • 尽管 HashMap 使用链表和红黑树来处理冲突,但冲突仍然会对性能产生显著影响:
    • 查询性能下降:当多个元素存储在同一个桶中时,查找这些元素的时间复杂度可能退化到 O(n),而不是理想情况下的 O(1)。
    • 插入性能下降:插入新元素时,如果发生冲突,可能需要遍历链表或调整红黑树,导致插入时间复杂度增加。
    • 内存使用效率:冲突频繁发生时,某些桶可能存储过多元素,而其他桶则几乎没有元素,这种不均匀性可能导致内存浪费。
    • 如果散列性不足,导致大量冲突,HashMap的性能将受到严重影响。在最坏的情况下,所有元素都可能映射到同一个桶,形成一个链表或红黑树,导致查找、插入和删除操作的时间复杂度退化到 O(n)。

1. HashMap 的基本概念

HashMap 使用一个数组来存储键值对(key-value pairs),每个键通过其哈希值计算出一个数组索引。理想情况下,不同的键应该映射到不同的索引,以避免哈希冲突。
数组:用于存储桶(bucket),每个桶可以存储多个键值对。
链表或红黑树:用于处理哈希冲突,桶中的每个元素可以是链表或红黑树。

1.1 数据结构

HashMap 的核心数据结构如下:

static class Node<K,V> {final int hash; // 哈希值final K key;    // 键V value;        // 值Node<K,V> next; // 下一个节点
}

2. 计算索引位置的流程

2.1 获取哈希值

当调用 put(key, value)get(key) 方法时,HashMap 首先会通过键的 hashCode() 方法获取哈希值。以下是 put 方法的简化实现:

public V put(K key, V value) {int hash = (key == null) ? 0 : hash(key);// 计算索引位置int index = indexFor(hash, table.length);// 进行插入操作
}

2.2 散列函数的实现

HashMap 中的散列函数是通过 hash 方法实现的。该方法的实现如下:

static final int hash(int h) {h ^= (h >>> 16); // 右移 16 位并异或return h ^ (h >>> 16); // 再次混合
}

2.3 计算索引位置

HashMap 中,索引位置的计算是通过以下方式实现的:

int index = (n - 1) & hash;

这里,n 是数组的长度,hash 是经过处理的哈希值。

3. 右移与异或的作用

3.1 为什么需要右移与异或?

在进行哈希值的计算时,HashMap 使用右移和异或的组合来增强哈希值的随机性。让我们通过一个示例来详细分析这一过程。

3.2 示例分析

假设我们有一个键 "key1",其哈希值为 0xB2690FF0。我们将分析右移和异或如何影响哈希值的混合。

原始哈希值
hash:  10110010 01101001 00001111 11110000  (二进制)

3.3 右移操作

首先,我们进行右移 16 位的操作:

hash >>> 16:
原始: 10110010 01101001 00001111 11110000
结果: 00000000 00000000 10110010 01101001

3.4 异或运算

接下来,我们进行异或运算:

new_hash = hash ^ (hash >>> 16);
计算过程
原始哈希值:  10110010 01101001 00001111 11110000
右移结果:    00000000 00000000 10110010 01101001
-----------------------------------------------
new_hash:     10110010 01101001 10110101 10011001

3.5 高位信息的参与

在计算索引时,HashMap 使用以下公式:

if ((p = tab[i = (n - 1) & hash]) == null) {// 插入新节点
}

这里,(n - 1) 是数组长度减去 1 的值。由于数组长度通常是 2 的幂(例如 16、32、64 等),n - 1 的二进制表示通常以 0 结尾,导致在进行按位与运算时,只有低位信息参与计算。

3.6 按位与的影响

因为按位与运算只有在对应的位都为 1 时结果才为 1,若 hash 的高位信息没有参与,可能导致多个不同的哈希值映射到同一个索引位置,从而增加哈希冲突的概率。例如:

  • 假设 n = 16,即 n - 1 = 15,其二进制为 0000 1111
  • 如果 hash 的高位为 0,低位的变化可能导致多个不同的 hash 值在与 15 进行与运算后得到相同的索引。

3.7 通过右移与异或减少冲突

通过右移和异或,HashMap 能够打破哈希值的模式,使得高位信息参与到最终的哈希计算中。这种方式使得即使在低位相同的情况下,高位的不同也能影响最终的索引计算,从而有效减少哈希冲突的发生。

4. 减少哈希冲突的原理

4.1 高位与低位的混合

通过右移和异或,HashMap 有效地将高位信息引入低位,增强了哈希值的随机性,使得相似的键在哈希表中更可能映射到不同的索引位置。

4.2 均匀分布

理想的散列函数应该能够均匀地分布哈希值。如果哈希值的分布不均匀,某些桶可能会存储过多的元素,从而导致性能下降。通过混合高位和低位,HashMap 提高了哈希值的随机性,使得键值对能够更加均匀地分布在桶中。

4.3 冲突处理机制

尽管 HashMap 通过上述方法减少了哈希冲突,但仍然可能发生冲突。当多个键映射到同一个索引时,HashMap 使用以下两种方法来处理冲突:

a. 链表法

在同一个桶中使用链表存储所有具有相同索引的键值对。当发生冲突时,新元素会被添加到链表的末尾。

b. 红黑树

当某个桶中的元素数量超过一定阈值(如 8),HashMap 会将链表转换为红黑树,以提高查找效率。红黑树的查找时间复杂度为 O(log n),比链表的 O(n) 更高效。

5. 总结

通过对 HashMap 源码的分析,我们可以看到它是如何计算索引位置的,以及散列函数在其中的关键作用。右移和异或的组合使用有效地混合了哈希值的高位和低位,从而减少了哈希冲突的概率,尽可能的确保了 HashMap 的高效性能。如果对你有帮助欢迎点赞支持。

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

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

相关文章

overleaf较高级的细节指令

换行命令 原来代码是将三个矩阵表达式在同一行显示&#xff0c;使用aligned环境&#xff08;需引入amsmath宏包&#xff0c;一般文档导言区默认会引入&#xff09;&#xff0c;把三个矩阵的定义分别放在不同行&#xff0c;可通过\\换行。 对齐命令 &放在等号前&#xff0…

LiteLLM:统一API接口,让多种LLM模型调用如臂使指

在人工智能迅猛发展的今天,各种大语言模型(LLM)层出不穷。对开发者而言,如何高效集成和管理这些模型成为一个棘手问题。LiteLLM应运而生,它提供了一个统一的API接口,让开发者可以轻松调用包括OpenAI、Anthropic、Cohere等在内的多种LLM模型。本文将深入介绍LiteLLM的特性、…

Google语法整理

以下是从整理出的 Google 语法&#xff1a; site&#xff1a;指定域名&#xff0c;如 “apache site:bbs.xuegod.cn”&#xff0c;可查询网站的收录情况 。 inurl&#xff1a;限定在 url 中搜索&#xff0c;如 “inurl:qq.txt”&#xff0c;可搜索 url 中包含特定内容的页面&a…

python 写一个工作 简单 番茄钟

1、图 2、需求 番茄钟&#xff08;Pomodoro Technique&#xff09;是一种时间管理方法&#xff0c;由弗朗西斯科西里洛&#xff08;Francesco Cirillo&#xff09;在 20 世纪 80 年代创立。“Pomodoro”在意大利语中意为“番茄”&#xff0c;这个名字来源于西里洛最初使用的一个…

Compose Multiplatform iOS 稳定版发布:可用于生产环境,并支持 hotload

随着 Compose Multiplatform 1.8.0 的发布&#xff0c;iOS 版本也引来的第一个稳定版本&#xff0c;按照官方的原话&#xff1a;「iOS Is Stable and Production-Ready」 &#xff0c;而 1.8.0 版本&#xff0c;也让 Kotlin 和 Compose 在移动端有了完整的支持。 在 2023 年 4 …

Jenkins 服务器上安装 Git

安装 Git # 更新包列表 sudo apt update# 安装 Git sudo apt install git 验证安装 # 检查 Git 版本 git --version 查看所有全局配置 git config --global --list 查看特定配置项 # 查看用户名配置 git config --global user.name# 查看邮箱配置 git config --global u…

OpenHarmony SystemUI开发——实现全局导航栏和状态栏关闭

在实际生产中&#xff0c;进场遇到需要关闭导航栏和状态栏的需求&#xff0c;现分享解决办法&#xff1a; 开发环境 OpenHarmony 5.0.0r 代码分析 思路&#xff1a; launcher本身可以关闭 导航栏&#xff08;实际是 公共事件&#xff0c;发送消息给systemUI来实控制&#x…

大模型微调终极方案:LoRA、QLoRA原理详解与LLaMA-Factory、Xtuner实战对比

文章目录 一、微调概述1.1 微调步骤1.2 微调场景 二、微调方法2.1 三种方法2.2 方法对比2.3 关键结论 三、微调技术3.1 微调依据3.2 LoRA3.2.1 原理3.2.2 示例 3.3 QLoRA3.4 适用场景 四、微调框架4.1 LLaMA-Factory4.2 Xtuner4.3 对比 一、微调概述 微调&#xff08;Fine-tun…

单片机-STM32部分:10-2、逻辑分析仪

飞书文档https://x509p6c8to.feishu.cn/wiki/VrdkwVzOnifH8xktu3Bcuc4Enie 安装包如下&#xff1a;根据自己的系统选择&#xff0c;目前这个工具只有window版本哦 安装方法比较简单&#xff0c;都按默认下一步即可&#xff0c;注意不要安装到中文路径哦。 其余部分参考飞书文档…

uniapp-商城-48-后台 分类数据添加修改弹窗bug

在第47章的操作中&#xff0c;涉及到分类的添加、删除和更新功能&#xff0c;但发现uni-popup组件存在bug。该组件的函数接口错误导致在小程序中出现以下问题&#xff1a;1. 点击修改肉类名称时&#xff0c;回调显示为空&#xff0c;并报错“setVal is not defined”&#xff0…

STM32-ADC模数转换器(7)

目录 一、ADC简介 二、逐次逼近型ADC 三、ADC基本结构图 四、规则组的四种转换模式 五、转换时间 对GPIO来说&#xff0c;它只能读取引脚的高低电平&#xff0c;使用了ADC模数转化器之后&#xff0c;就可以对高电平和低电平之间的任意电压进行量化&#xff0c;最终用一个变…

智能商品推荐系统技术路线图

智能商品推荐系统技术路线图 系统架构图 --------------------------------------------------------------------------------------------------------------- | 用户交互层 (Presentation Layer) …

【Docker系列】docker inspect查看容器部署位置

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

标量/向量/矩阵/张量/范数详解及其在机器学习中的应用

标量&#xff08;Scalar&#xff09;、向量&#xff08;Vector&#xff09;、矩阵&#xff08;Matrix&#xff09;、张量&#xff08;Tensor&#xff09;与范数&#xff08;Norm&#xff09;详解及其在机器学习中的应用 1. 标量&#xff08;Scalar&#xff09; 定义&#xff1…

【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本(非常简洁快速)

【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是springboot的使用。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&…

SierraNet协议分析使用指导[RDMA]| 如何设置 NVMe QP 端口以进行正确解码

在解码RoCEv2数据包&#xff08;包括TCP RDMA和RoCE RDMA&#xff09;时&#xff0c;若捕获的跟踪数据无法正确解码&#xff0c;通常需要执行特定的解码步骤。对于RoCE RDMA跟踪数据的处理&#xff0c;分析器主要采用两种方式获取必要信息以实现数据包解码&#xff1a; 首先&am…

JavaScript基础-局部作用域

在JavaScript中&#xff0c;理解不同种类的作用域是掌握这门语言的关键之一。作用域决定了变量和函数的可访问性&#xff08;即可见性和生命周期&#xff09;。与全局作用域相对应的是局部作用域&#xff0c;它限制了变量和函数只能在其定义的特定范围内被访问。本文将深入探讨…

李沐动手深度学习(pycharm中运行笔记)——09.softmax回归+图像分类数据集+从零实现+简洁实现

09.softmax回归图像分类数据集从零实现简洁实现&#xff08;与课程对应&#xff09; 目录 一、softmax回归 1、回归 vs 分类 2、经典分类数据集&#xff1a; 3、从回归到分类——均方损失 4、从回归到多类分类——无校验比例 5、从回归到多类分类——校验比例 6、softmax和…

C++八股——内存分配

文章目录 1. 虚拟内存空间2. malloc和free3. new和delete4. 内存池 1. 虚拟内存空间 程序进程的虚拟内存空间是操作系统为每个进程提供的独立、连续的逻辑地址空间&#xff0c;与物理内存解耦。其核心目的是隔离进程、简化内存管理&#xff0c;并提供灵活的内存访问控制。 &am…

【Linux基础】网络相关命令

目录 netstat命令 1.1 命令介绍 1.2 命令格式 1.3 常用选项 1.4 常用命令实例 1.4.1 显示所有TCP连接 1.4.2 查看路由表 1.4.3 实时监控网络接口流量 1.4.4 查看监听中的端口以及关联进程 ping命令 2.1 命令介绍 2.2 命令格式 2.3 常用选项 2.4 常用示例 ifconfi…