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 ...
随机推荐
- Hibernate配置文件和映射文件详解
Hibernate是一个彻底的ORM(Object Relational Mapping,对象关系映射)开源框架. 我们先看一下官方文档所给出的,Hibernate 体系结构的高层视图: 其中PO=P ...
- CSS表单与数据表(上)
表单在现代Web应用中占据着重要地位. 表单可以很简单,也可以非常复杂,要横跨几个页面. 除了从用户哪里获得数据,Web应用还需要以容易看懂的方式展示数据.表格是展示复杂数据的最佳方式. 1.设计数据 ...
- 【av68676164(p21-p22)】线程
4.3.1 线程概念 线程的概念(Thread) 线程是可由CPU直接运行的实体 一个进程可以创建多个线程 多个线程可共享CPU可以实现并发运行 CreateThread() 功能:把一个函数创建为一 ...
- CNN 小结
CNN 小结 目录 CNN特征提取过程(卷积核描述的是特征信息, 此特征可能就是原图像中的某些像素, 但是卷积核并不找相似的地方在原始图像的哪里, 所以需要将卷积核不断地滑动, 得到的feature ...
- 【lhyaaa】2020深圳大湾区比赛总结
又名花样丢分锦集 首先,我们需要来吐槽一下垃圾szccf,毁我青春 居然没有提醒不能用万能头文件,导致一题爆0,据说还有一些结果不是自己答的 emmm-- 进正题 选择题 对包含n个数的序列进行冒泡排 ...
- IndexFlatL2、IndexIVFFlat、IndexIVFPQ三种索引方式示例
上文针对Faiss安装和一些原理做了简单说明,本文针对标题所列三种索引方式进行编码验证. 首先生成数据集,这里采用100万条数据,每条50维,生成数据做本地化保存,代码如下: import numpy ...
- FPN和他的子孙们
FPN 方框里表示top down里每层有两个卷积操作 PAN:添加一个 bottom up线 NAS-FPN:基于搜索结构的FPN Fully-conencted FPN:全连接的FPN Si ...
- 方法解析之Method与ConstMethod介绍
HotSpot通过Method与ConstMethod来保存方法元信息. 1.Method Method没有子类,定义在method.hpp文件中,其类继承关系如下图: Method用于表示一个Jav ...
- db2 创建function报错
create function fun_fw_sfyczy(pi_operunitid varchar(2)) returns varchar(2)LANGUAGE SQL BEGIN ATOMIC ...
- java final关键字与static关键字
一 final关键字 1.final修饰类不可以被继承,但是可以继承其他类. 例如: class Yy {} final class Fu extends Yy{} //可以继承Yy类 class ...