HashMap位运算你可知一二
前置位运算知识
我们平时在写代码过程中用的位运算操作比较少,因为我们更关注于可读性而不是性能,如果为了性能而使用较多的位运算,我想我们的同事会疯掉。但在框架里位运算却非常常见,因为框架的性能是我们关注的点。下面就来一起回顾一下常见的位运算操作:
<< : 左移运算符,num << 1,相当于num乘以2 低位补0
>> : 表示右移,如果该数为正,则高位补 0,若为负数,则高位补 1。
>>> : 表示无符号右移,也叫逻辑右移,即若该数为正,则高位补 0,而若该数为负数,则右移后高位同样补 0。
% : 模运算 取余
^ : 位异或 第一个操作数的的第n位于第二个操作数的第n位相反,那么结果的第n为也为1,否则为0
& : 与运算 第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0
| : 或运算 第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0
~ : 非运算 操作数的第n位为1,那么结果的第n位为0,反之,也就是取反运算(一元操作符:只操作一个数)
HashMap的hash函数算法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
此时我们心中会有两个疑惑:
- 为什么要无符号右移 16 位后做异或运算
- key 本身的 hashCode 直接拿来用不行吗
来看这样一个例子:
将 h 无符号右移 16 为相当于将高区 16 位移动到了低区的 16 位,再与原 hashcode 做异或运算,可以看作是将高低位二进制特征混合起来
。
从上图中可以看出,高位的 16 位与原 hashcode 相比没有发生变化,低位的 16 位发生了变化。
上面的 (h = key.hashCode ()) ^ (h >>> 16) 进行运算后,可以把高区与低区的二进制特征混合到低区,那么为什么要这么做呢?
我们要知道,上面计算出来的hashcode值接下来要参与到hashmap中数组槽位的计算,其计算公式是:(n - 1) & hash,现在假设数组槽位大小是16,那么槽位计算过程如下:
观察可以看出,如果我们不做刚才移位异或运算,那么在计算槽位时将丢失高区特征。也许你可能会说,即使丢失了高区特征,不同 hashcode 也可以计算出不同的槽位来,但是细想当两个哈希码很接近时,那么这高区的一点点差异就可能导致一次哈希碰撞,所以这也是将性能做到极致的一种体现。
为什么要采用异或运算
异或运算能更好的保留各部分的特征,如果采用 & 运算计算出来的值会向 1 靠拢,采用 | 运算计算出来的值会向 0 靠拢。
为什么槽位数必须使用 2^n
这里假设槽位数不是 16,而是 17,那么槽位计算公式就变成:(17 - 1) & hash。
可以看出计算结果将会大大趋同,hashcode 参加 & 运算后被更多位的 0 屏蔽,计算结果只剩下两种,分别是0 和 16,这对于 hashmap 来说是一种灾难。
总结
HashMap当中运用了很多精巧的位运算操作,这对于提高性能有很大帮助,更多的,很多的优化点,最终目的还是为了让哈希后的结果更均匀的分部,减少哈希碰撞,提升 hashmap 的运行效率。
参考文章
[1] https://zhuanlan.zhihu.com/p/149583558
[2] https://juejin.im/entry/5e1a960d5188254c257c38e5
[3] https://www.jianshu.com/p/eb9ab4211163
HashMap位运算你可知一二的更多相关文章
- 我们必须要了解的Java位运算(不仅限于Java)
本文原创地址为 https://www.cnblogs.com/zh94/p/16195373.html 原创声明:作者:陈咬金. 博客地址:https://www.cnblogs.com/zh94/ ...
- HDU 4949 Light(插头dp、位运算)
比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...
- Java位运算总结:位运算用途广泛《转》
前天几天研究了下JDK的Collection接口,本来准备接着研究Map接口,可是一查看HashMap类源码傻眼咯,到处是位运算实现,所以我觉得还是有必要先补补位运算知识,不然代码看起来有点费力.今天 ...
- Java学习之位运算和逻辑运算符
今天看了一下HashMap类的源码,在HashMap的源码中定义了初始空间的大小 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 当 ...
- C语言学习笔记之位运算求余
我们都知道,求一个数被另一个数整除的余数,可以用求余运算符”%“,但是,如果不允许使用求余运算符,又该怎么办呢?下面介绍一种方法,是通过位运算来求余,但是注意:该方法只对除数是2的N次方幂时才有效. ...
- Python语言中的按位运算
(转)位操作是程序设计中对位模式或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加 ...
- java位运算(操作)的使用
位操作是程序设计中对位模式按位或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加法 ...
- 136.Single Number---异或、位运算
题目链接 题目大意:给出一串数组,里面的数都是两个,只有一个数是一个,把这个只有一个的数找出来.时间复杂度最好是线性的,空间复杂度最好为O(1). 法一:利用map,空间换时间,代码如下(耗时26ms ...
- string与位运算
1.String String a="abc"; 会在常量池中开辟一个空间,保存"abc" String b=new String("abc&q ...
随机推荐
- ios企业签名为什么会掉签?
我们都知道ios用户无法直接安装App Store之外的应用,对于那些无法上架苹果应用商店的APP,开发者们一般会选择苹果签名的形式. 目前的苹果签名有ios企业签名.超级签名和TF上架这三种 ...
- 使用Azure人脸API对图片进行人脸识别
人脸识别是人工智能机器学习比较成熟的一个领域.人脸识别已经应用到了很多生产场景.比如生物认证,人脸考勤,人流监控等场景.对于很多中小功能由于技术门槛问题很难自己实现人脸识别的算法.Azure人脸API ...
- Linux系统中玩到让你停不下来的命令行游戏!
大家好,我是良许. 在使用 Linux 系统时,命令行不仅可以让我们在工作中提高效率,它还可以在生活上给我们提供各种娱乐活动,因为你可以使用它玩许多非常有意思的游戏,这些游戏可都不需要使用专用显卡. ...
- “随手记”开发记录day10
今天完成了各个demo合并,并进行测试,中间出现了一些bug, 例如再点击主页面预留的菜单按钮时会出现闪退 在手机上运行更新页面时会直接崩溃 还有发现的问题有,不能查看往期的记录
- 一张图理清 Python3 所有知识点
如果你前几天一直有关注 GitHub Trending,那你应该会留意到「Python3 in one pic」这个开源项目. 很多人学习python,不知道从何学起.很多人学习python,掌握了基 ...
- javascript 简单、繁杂类型、栈、堆笔记
简单数据类型 值类型:在存储变量中的是值本身 简单数据类型 null返回的是空的对象 string,number,Boolean,undefined,null 繁杂数据类型 ...
- Linux中文解决
中文编码问题 安装中文语言包 locale -a | grep zh 查看是否有中文语言包 local 查看是否 LC_TYPE 为空 在 /etc/profile.d/ 下创建 lc_type.sh ...
- 《Spanner: Google’s Globally-Distributed Database》论文总结
Spanner 总结 说明:本文为论文 <Spanner: Google's Globally-Distributed Database> 的个人理解,难免有理解不到位之处,欢迎交流与指正 ...
- java_流程控制语句、权限修饰符
判断语句 if语句第一种格式: if if(关系表达式){ 语句体; } if语句第二种格式: if…else if(关系表达式) { 语句体1; } else { 语句体2; } if语句第三种格式 ...
- C#LeetCode刷题之#40-组合总和 II(Combination Sum II)
目录 问题 示例 分析 问题 该文章已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3666 访问. 给定一个数组 candidates ...