-
Java连载152-HashMap中的hash函数有什么用
一、取模运算和取余运算
-
取余运算,这个很好理解,我们经过多年的数学学习也知道,就是求余数,一个整数和另一个整数相除,得到它们的余数,就是我们说的取余 -
取模运算,通俗的来讲大多运算在计算机领域,取模运算其实就是两个二进制数字之间做与运算,它们最后得到的数字就是取模 -
我们举个简单的例子,有一个二进制数字0000 0001 1001 1101,1111 0101 1010 0011,这个两个数字做与运算,它们相同位置的数字,如果有一个数字出现1,那么计算后的数字的那个位置就是1,这两个数字与运算后的值为1111 0101 1011 1111这就是取模运算
二、hash函数在HashMap的源码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
-
我们乍一看这个东西很懵,到底有什么用,讲这个源码之前,我们得先知道hashCode()函数是干嘛的?我们先看源码
@HotSpotIntrinsicCandidate
public native int hashCode();
-
这个就是hashCode函数的实现,从native关键字可以看出这是一个由底层C代码实现的函数,因此我们不必知道它是怎么实现,我们只需要知道,它就是返回一个int类型的数字,这个数字就相当于对象的身份证,指纹,因此可以利用这个“身份证”来进行归类,我们知道int值域在-2147483648 到 2147483648,这个范围有40多亿,因此想要相同也不容易,但是我们的HashMap底层数据结构是一个数组啊,用于存储这些节点,初始大小为16,因此按照我们常规做法就是,用这个“身份证”和数组大小(也就是16)做取余运算,这样我们就是把一个很大的数字映射成一个16以内的整数。然后我们就可以把这个节点存储到这个数组的某一个位置了。
三、hashMap中映射的解决方案
-
这个解决方案其实很好,但是我们还没有其他的解决方案呢? -
其实是有的,在hashmap中的做映射,使用是取模运算而不是取余运算,这是为啥? -
因为取余运算的效率远高于取余运算,我们知道任何数据存储在计算机内部都是以二进制形式存储的,因为两个int整数进行取模运算的时候,做几次比较立即就得出了,但是取余就要运算很长 -
hashMap内部使用取模,比如我们一个对象的“身份证”是 -
1010 0111 0100 1010 1111 1001 0010 1010 -
我们数组大小是16,16的二进制是0000 0000 0000 0000 0000 0000 0001 0000 -
我们减一,那就是 0000 0000 0000 0000 0000 0000 0000 1111,用这个数字和上面那个“身份证”取模得到0000 * 7个,最后四位就是1010,多好啊!把一个数字映射成了一个16以内的数字,我们看看源码是不是这个方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
/// 后面省略了
-
可以看见正好n-1就是16-1=15这样2进制最后4位都是1,就是模取出来了 -
这也正好解释了为什么扩容都是*2了,因为-1之后就是后面几位数字都是1了,便于好取模,求出映射 -
回到我们刚才说的hash函数有什么用 -
我们看这里h&(h>>16),其实就是身份证和身份证往右移动16位之后取模,我们知道一个int值类型就是一个32位的二进制,这样其实就是把前16和后16位进行取模,这样就把一个增大了随机性,因为你看原来的方法只取了后面4位,前面的28位都没啥用,用这个方法之后,再取后4位就是把前面的16位也拿来了 -
总结:hash函数就是为了是对象的hash值增加随机性,它集合前16位和后16的特征进行计算,这样再被取模之后,会让对象在HashMap中分布更均匀。
四、源码:
-
github路径:https://github.com/ruigege66/Java/blob/master/newJava/src/com/newJava/D151_HashMapAnalysis.java -
CSDN:https://blog.csdn.net/weixin_44630050
出 处:https://www.cnblogs.com/ruigege0000/p/15700598.html
最新更新
带有参数的装饰器
类装饰器
django中的auth模块与admin后台管理
python的日期处理
字符串常用方法
基本数据类型概述
python-map()函数基本用法
python带你实现任意下载AcFun视频数据~
bbs项目之注册功能
变量的定义和使用
三大常用数据库事务详解之三:事务运行
三大常用关系型数据库事务详解之二:基
三大关系型数据库事务详解之一:基本概
MongoDB常用命令(2)
MongoDB基本介绍与安装(1)
SQLServer触发器调用JavaWeb接口
SQL Server索引的原理深入解析
SqlServer2016模糊匹配的三种方式及效率问题
SQL中Truncate的用法
sqlserver 多表关联时在where语句中慎用tri
VB.NET中如何快速访问注册表
ASP.NET中图象处理过程详解
Vue(1)Vue安装与使用
JavaScript 语言入门
js将一段字符串的首字母转成大写
纯原生html编写的h5视频播放器
H5仿原生app短信验证码vue2.0组件附源码地
TypeScript(4)接口
TypeScript(3)基础类型
TypeScript(2)WebStorm自动编译TypeScript配置