HashMap中的为什么hash的长度为2的幂而&位必须为奇数

蔚来一面:HashMap 的 hash 方法原理是什么?看完这篇还不懂HashMap的hash原理,那我要哭了~ Warning:这是《Java 程序员进阶之路》专栏的第 55 篇。那天,小二去蔚来面试,面试官老王一上来就问他:HashMap 的 hash 方法的原理是什么?当时就把裸面的小二给蚌埠住了。 回来后小二找到了我,于是我就写下了这篇文章丢给他,并严厉地告诉他:再搞不懂就别来找我。听到这句话,心头一阵酸,小二绷不住差点要哭 ????。 PS:本文 GitHub 上已同步,有 GitHub 账号的小伙伴,记得看完后给二哥安排一波 star 呀!冲一波 GitHub 的 trending 榜单,求求各位了。 G 阅读详情

背景

哈希算法在Java中是经常用的的一个算法,也是一些常用数据结构中必用的一个算法,它为上层的复杂数据结构提供了基础支撑。

哈希算法的实现有很多种,除了这里讲的map中的hashcode算法,还有其他哈希算法:

1.直接定址法
2.数字分析法
3.折叠法
4.平方取中法
5.减去法
6.字符串数值哈希法
7.旋转法

更多算法请参考另一篇作者的文章:
hash算法原理详解

HashMap中的HashCode算法详解

1,哈希算法在HashMap类中的应用
java中的集合,比如HashMap/Hashtable/HashSet等,在实现时,都用到了哈希算法。当我们向容器中添加元素时,我们有必要知道
这个元素是否已经存在。
从实现上来说,java是借助hashcode()方法和equals()方法来实现判断元素是否已经存在的。当我们向HashMap中插入元素A时,首先,
调用hashcode()方法,判断元素A在容器中是否已经存在。如果A元素的hashcode值在HashMap中不存在,则直接插入。否则,接着调用
equals()方法,判断A元素在容器中是否已经存在。hashcode()的时间复杂度为O(1),equals()方法的时间复杂度为O(m),整体的时间复杂度
就是:O(1) + O(m)。其中,m是桶的深度。桶的深度是一个什么概念呢,桶的深度是指具有相同hashcode值得元素的个数,也就是发生哈希
碰撞的元素的个数。
一个好的哈希算法应该尽量减少哈希碰撞的次数。

HashCode是Object中本身就有的方法,但是没有具体实现。而各个不同的数据类型又继承实现了各自的具体HashCode算法,这里只以String类型的HashCode为例。

public int hashCode() {  
int h = hash;  
if (h == 0) {  
    int off = offset;  
    char val[] = value;  
    int len = count;  

        for (int i = 0; i < len; i++) {  
            h = 31*h + val[off++];  
        }  
        hash = h;  
    }  
    return h;  
}  

源代码写的比较简洁,阅读起来也不是太方便,下面我详细解读一下:
// String类的hashcode值(哈希值)是如何计算得到的?具体实现?为了方便阅读,我们来进行分步说明

static void hashcodeTest(){  

    String str = "yangcq";  

    // 第一步 = (int)'y'  
    // 第二步 = (31 * (int)'y') + (int)'a'  
    // 第三步 = 31 * ((31 * (int)'y') + (int)'a') + (int)'n'  
    // 第四步 = 31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g'  
    // 第五步 = 31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c'  
    // 第六步 = 31 * (31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c') + (int)'q'  

    // 上面的过程,也可以用下面的方式表示  

    // 第一步 = (int)'y'  
    // 第二步 = 31 * (第一步的计算结果) + (int)'a'  
    // 第三步 = 31 * (第二步的计算结果) + (int)'n'  
    // 第四步 = 31 * (第三步的计算结果) + (int)'g'  
    // 第五步 = 31 * (第四步的计算结果) + (int)'c'  
    // 第六步 = 31 * (第五步的计算结果) + (int)'q'  

    int hashcode = 31 * (31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c') + (int)'q';  
    System.out.println("yangcq的hashcode = " + hashcode);        // yangcq的hashcode = -737879313  
    System.out.println("yangcq的hashcode = " + str.hashCode());  // yangcq的hashcode = -737879313  

}  

为什么HashMap中的&位必须为奇数(Length - 1)

从Key映射到HashMap数组的对应位置,会用到一个Hash函数:

index = Hash(“apple”)

如何实现一个尽量均匀分布的Hash函数呢?我们通过利用Key的HashCode值来做某种运算。
如何进行位运算呢?有如下的公式(Length是HashMap的长度):

index = HashCode(Key) & (Length - 1)

下面我们以“book”的Key来演示整个过程:

1.计算book的hashcode,结果为十进制的3029737,二进制的101110001110101110 1001。

2.假定HashMap长度是默认的16,计算Length-1的结果为十进制的15,二进制的1111。

3.把以上两个结果做与运算,101110001110101110 1001 & 1111 = 1001,十进制是9,所以 index=9。

可以说,Hash算法最终得到的index结果,完全取决于Key的Hashcode值的最后几位。

假设HashMap的长度是10,重复刚才的运算步骤:
这里写图片描述

单独看这个结果,表面上并没有问题。我们再来尝试一个新的HashCode 101110001110101110 1011 :
这里写图片描述
让我们再换一个HashCode 101110001110101110 1111 试试 :
这里写图片描述

是的,虽然HashCode的倒数第二第三位从0变成了1,但是运算的结果都是1001。也就是说,当HashMap长度为10的时候,有些index结果的出现几率会更大,而有些index结果永远不会出现(比如0111)!

这样,显然不符合Hash算法均匀分布的原则。

反观长度16或者其他2的幂,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。

【Unity3D】关于unity中spriteAtlas的打包方案及使用时内存状态的多组对照实验 unity项目中使用精灵(Sprite)为了降低性能消耗,我们可以使用精灵图集(Sprite Atlas)技术,它能够将多个纹理合并成一个大纹理 阅读详情

相关推荐

告别卡顿!用LVGL+DMA刷新STM32F407的SPI屏幕,性能提升实战

本文详细介绍了如何通过LVGL+DMA技术优化STM32F407驱动SPI屏幕的性能,实现接近60FPS的流畅刷新。从硬件瓶颈分析到SPI与DMA的精细化配置,再到LVGL的双缓冲机制与局部刷新优化,提供了一套完整的性能提升方案。特别适合嵌入式GUI开发中面临卡顿问题的工程师参考。

weixin_42531588的博客 378

HashMap的默认初始长度是多少?为什么?

1.HashMap的默认初始长度是16,自动拓展和手动初始化时,长度必须是2的幂,即2^n (每次扩容都是以2的整数次幂扩容 2.原因:选择16是为了服务于从Key映射到index的Hash算法,在性能和内存的使用上取平衡,实现一个尽量均匀分布的Hash函数,选取16,是通过位运算的方法进行求取的。 3.Hash函数:index=Hash(Value) 4.位运算求HashMap函数,位运算:index=Hash数据(Key)&amp;(length-1),进行的是二进制的与(&amp;)运算 例如

qq_49945182的博客 8044

ROS2机械臂MPC轨迹规划实战:从MoveIt 2插件配置到参数调优全流程

本文详细介绍了ROS2机械臂MPC轨迹规划的全流程,从MoveIt 2插件配置到参数调优。通过模型预测控制(MPC)技术,实现高精度运动控制,特别适合复杂装配任务。文章包含环境搭建、插件部署、参数设计及实战调优方法,帮助开发者快速掌握ROS2机械臂的MPC轨迹规划技术。

i0j1k2l3m的博客 206

为啥HashMap的默认容量是16?

集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生。 在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;(); 但是,大家有没有想过,上面的代码中,我们并没有给HashMap指定容量,那么,这时候一个新创建的HashMap的默认容量是多少呢?为什么呢? 本文就来分析下这个问题。 什

feel369的博客 2732

利用LTspice设计Pt100测温电路

是一种广泛使用的温度传感器,它属于(RTD, Resistance Temperature Detector)的类型,通常用于温度测量。在0°C时,的电阻值为100欧姆,且它的电阻值会随着温度的升高而升高。PT100热电阻温度阻值对应表)因此可以根据这一特性来检测出环境温度。

2301_77033628的博客 2412

RM机器人麦轮底盘实战:从零搭建到PID调参全记录(附避坑指南)

本文详细记录了RM机器人麦轮底盘从零搭建到PID调参的全过程,涵盖硬件组装、开发环境搭建、底盘解算和PID调参等关键步骤,并附有实用的避坑指南。特别针对麦轮控制、底盘解算和电机调试等核心问题提供了专业解决方案,帮助开发者快速提升机器人性能。

bush的博客 369

hasMap底层原理

hashMap底层原理

rwh19951022的博客 180

【常见开源库的二次开发】基于openssl的加密与解密——openssl认识与配置(一)

OpenSSL 是一个开源的软件库,它提供了一系列加密工具和协议,主要用于实现安全通信,如在网络上的数据传输。它支持多种加密算法,包括对称加密、非对称加密、散列函数、伪随机数生成器、数字签名、密钥交换以及证书管理等。

嵌入式技术开发 4160

HashMap为啥初始化大小是16

HashMap的默认初始化长度是多少? static final int DEFAULT_INITIAL_CAPACITY = 1 &lt;&lt; 4; // aka 16 在JDK1.8的 235 行有1&lt;&lt;4就是16,为啥用位运算呢?直接写16不好么?这里主要是位运算的性能好,为啥位运算性能就好,那是因为位运算人家直接操作内存,不需要进行进制转换,要知道计算机可是以二进制的形式做数据存储啊, 这里使用了位运算,为啥不直接16嘞?这里主要是位运算的性能好,为啥位运算性能就好,那是因

Restless Breeze 8159

基于Qt C++开发一个体测数据管理系统

advice.dietAdvice = "饮食建议:\n1. 控制总热量摄入,每天减少300-500大卡\n2. 减少精制碳水(白米饭、白面包、甜食)\n3. 增加优质蛋白和膳食纤维摄入\n4. 烹饪方式以蒸、煮、烤、凉拌为主,减少油炸\n5. 晚餐适量减少,睡前3小时避免进食";dataTable->setHorizontalHeaderLabels({"ID", "用户名", "身高(cm)", "体重(kg)", "BMI", "体脂率(%)", "记录时间"});

寒寒豆豆软件公司的博客 192

Android Telephony主要模块及功能

Telephony的代码主要由五个模块组成 1.Telephony Frameworks 代码区域: frameworks/opt/telephony/ 其中主要的文件在如下目录 /frameworks/opt/telephony/src/java/com/android/internal/telephony/ 其中比较常见的有以下几类文件 1.BaseCommands、RIL构成R

泊客 7720

hashMap的数组长度,加载因子及转红黑树的阈值解析

hashMap的数组长度为什么要求是2的整数次幂 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,...

qq_42524262的博客 2881

图像处理 | 基于matlab的多尺度Retinex(MSR)和自适应直方图均衡化(CLAHE)算法联合的低照度图像增强(附代码)

本文介绍了一种基于多尺度Retinex(MSR)和CLAHE的低照度图像增强算法。算法首先对图像进行多尺度Retinex处理,通过不同尺度的高斯核去除光照影响;然后采用CLAHE算法进行对比度增强。Matlab实现展示了算法流程,包含灰度转换、多尺度Retinex增强和CLAHE处理。实验结果显示,该方法能有效提升低照度图像的可见性,增强细节表现。最后提出了改进建议,包括引入深度学习方法和优化参数选择。

尘世冰封的专栏 952

HashMap中为什么Hash的长度为2的幂而&位必须为奇数

从Key映射到HashMap数组的对应位置,会用到一个Hash()函数: index = Hash(“apple”) 为了实现一个尽量分布均匀的Hash()函数,我们通过利用Key的HashCode值来做某种运算。 index = HashCode(Key) &amp; (Length -1) //Length是HashMap的长度 Hash算法最终得到的index结果,完全取决于Ke...

qq_44863974的博客 420

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

为什么HashMap的长度一定是2的次幂?

HashMap是面试过程中最常问的知识点之一 今天用最通俗易懂的大白话来讲一讲:为什么HashMap的长度一定是2的次幂? 大家知道HashMap中,如果想存入数据,首先它需要根据key的哈希值去定位落入哪个桶中 它的做法是,三步:&gt;&gt;&gt;无符号右移、^异或、&amp;与 具体是:拿着key的哈希值,先无符号右移16位,然后异或上key的哈希值,得到一个值,再拿着这个值去与上数组长度减一 最后得出一个数,如果数组长度是15的话,这个数是一个0-15之间的一个数,这个数就是得出的数组

曹举的个人博客 7859

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

为什么HashMap的长度是2的整数次幂?

不过你说的是解释了源码右移背后的意义,没说为什么选择2^n, 其实你就只差一步了: 正是因为hash值就是要用低位的信息,那么结合&操作,&的另一个数最好低位全是1,这样&才有意义;,length-1 为奇数,奇数的二进制最后一位是 1,这样便保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,因此,length 取 2 的整数次幂,是为了使不同 hash 值发生碰撞的概率较小,这样就能使元素在哈希表中。

daobuxinzi的博客 213

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【HashMap】为什么长度总是2的整数次方

前言 开门见山,HashMap这样做有两点原因 提升计算效率,更快算出元素的位置 减少哈希碰撞,使得元素分布均匀 提升计算效率 我们先看put方法的细节: public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } 其中hash(key)如下: static final int hash(Object key) { int h;

阳阳的博客 1712

hashmap为什么是2的倍数_为什么HashMap的长度是2的整数次幂?

先说答案:为了加快哈希计算以及减少哈希冲突为什么可以加快计算?我们都知道为了找到 KEY 的位置在哈希表的哪个槽里面,需要计算 hash(KEY) % 数组长度但是!% 计算比 &amp; 慢很多所以用 &amp; 代替 %,为了保证 &amp; 的计算结果等于 % 的结果需要把 length 减 1也就是 hash(KEY) &amp; (length - 1)这个 hash(KEY) 没什么可...

weixin_33900697的博客 584

HashMap中数组下标的计算,为什么HashMap的容量是2的倍数?

length-1 为奇数,奇数的二进制最后一位是 1,这样便保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,比如说 9 % 4 = 1,9 的二进制是 1001,4 - 1 = 3,3 的二进制是 0011,9 & 3 = 1001 & 0011 = 0001 = 1。比如说,数组长度是 3,hash 是 7,那么 7 % 3 的结果就是 1,也就是此时可以把元素放在下标为 1 的位置。进行&运算结果为2。

qq_52822387的博客 6591

15.为什么HashMap的容量是2的幂次方

Java后端面试八股文:HashMap

练习生 913

若Java设计“非2的幂”哈希表:需要攻克的五大核心难题

如果你是Java设计者,面对“非2的幂哈希表”的需求,你会优先解决哪个问题?会将用户指定的初始容量自动转为≥该值的最小2的幂(如指定10→16),底层逻辑清晰。:如何在非2的幂场景下,既保证桶定位效率,又充分利用哈希值的全量信息?扩容(2倍扩容)时,元素的新桶位置只需判断旧容量的最高位是否为1(即。:如何设计一种扩容算法,让非2的幂扩容也能实现“部分元素快速迁移”?(n为2的幂),利用二进制特性将取模运算转化为更高效的位运算。尽管非2的幂哈希表在理论上存在可能(如C++的。中,哈希桶的计算采用。

全栈开发工程师 1185

HashMap的默认长度为什么是16??

本文转载至:http://blog.youkuaiyun.com/zjcjavahttps://blog.youkuaiyun.com/zjcjava/article/details/78495416 背景 哈希算法在Java中是经常用的的一个算法,也是一些常用数据结构中必用的一个算法,它为上层的复杂数据结构提供了基础支撑。 哈希算法的实现有很多种,除了这里讲的map中的hashcode算法,还有其他哈希算法: ...

zhucegemingzizheng的博客 2万+

2的负x次幂图像_为什么HashMap的长度是2的整数次幂?

先说答案:为了加快哈希计算以及减少哈希冲突为什么可以加快计算?我们都知道为了找到 KEY 的位置在哈希表的哪个槽里面,需要计算 hash(KEY) % 数组长度但是!% 计算比 &amp; 慢很多所以用 &amp; 代替 %,为了保证 &amp; 的计算结果等于 % 的结果需要把 length 减 1也就是 hash(KEY) &amp; (length - 1)这个 hash(KEY) 没什么可...

weixin_29802189的博客 284

HashMap——HashMap的容量为什么是2的n次幂

由此我们可以看出当哈希数组长度为奇数时是极易发生哈希碰撞,导致我们的数组空间不能被充分利用且。过长,降低查找的效率而哈希数组长度为偶数时发生哈希碰撞的概率是比较小的,所以数组长度在考虑。来计算索引值,key&(length-1)这两种计算方式并无太大区别,只是相对于。为key%(length-1)但这样在计算机中计算效率比较低,所以我们采用。被设置为16,而在我们通过key去计算哈希值(索引值时)计算一般采用。我们在计算哈希值时发生哈希碰撞是不可避免的,但是怎样才能。位运算:都为1时则为1否则为0。

Lemon_man_的博客 398

HashMap的大小为什么必须是2的倍数

HashMap如何保证大小一定是2的倍数 了解HashMap的都知道,HashMap的大小必须是2的倍数,通过调用tableSizeFor方法来保证HashMap的大小为2的次方,如果你在构造函数中传入了大小,HashMap会调用tableSizeFor方法将大小变成大于你传入的数的最小2的倍数 HashMap如何将数据进行散列,保证数据的散列程度够大? 首先通过JVM支持的hashCode方法,得到一个 ...

OnlyloveCuracao的博客 5066

hash,hashcode,hashmap

关于hash hashcode等问题什么是hash什么是hashcodehashcode的值怎么得到的?为什么hashcode方法里要用31这个数字?hash的算法是什么hashmap的原理剖析put操作get操作resize流程 什么是hash 学过编程的人都听说过hash,哈希,哈希列表,哈希等等,但是hash到底是个啥? 摘自百度百科的解释: Hash,一般翻译做“散列”,也有直接音译为“哈...

qq_40103721的博客 257
上一篇: Docker第一篇:简介和入门安装
下一篇: Docker第二篇:镜像的基本使用-搭建Centos系统
牧竹子
博客等级 码龄17年 572粉丝 233原创
评论 18
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值