HashMap的哈希/扰动函数底层原理

哈希函数的目标

均匀分布:使不同的键能均匀地分布在哈希表中,减少哈希冲突,即尽量让不同的键计算出的哈希值分散在哈希表的不同位置,提高存储和查找效率。

高效计算:能够快速地计算出键的哈希值,以保证HashMap的插入、查找和删除等操作具有较好的性能。

初始哈希值计算

首先会调用键对象的 hashCode() 方法,该方法是 Object 类的方法,每个Java对象都有。若类没有重写 hashCode() ,则会根据对象的内存地址等信息生成一个默认哈希值。

很多类如 String 、 Integer 等都重写了 hashCode() 方法以实现更合理的哈希值计算。以 String 类为例,它根据字符串的字符序列计算哈希值,保证相同内容的字符串哈希值相同,不同内容的字符串哈希值尽量不同。

二次哈希(扰动函数)

得到的初始哈希值是⼀个32位的int类型的数值,然后让hashcode的⾼ 16位和低16位进⾏异或操作,使得哈希值的高位和低位都参与运算,进一步打乱哈希值,使其分布更均匀。

异或操作运算如下:

同时,这么设计也是为了降低哈希碰撞的概率。

索引计算

因为 key.hashCode() 函数调用的是 key 键值类型⾃带的哈希函数,返回 int 型散列值。int 值范围 为 -2147483648~2147483647,加起来⼤概 40 亿的映射空间。

只要哈希函数映射得⽐较均匀松散,⼀般应⽤是很难出现碰撞的。但问题是⼀个 40 亿长度的数组,内 存是放不下的。

假如 HashMap 数组的初始⼤⼩才 16,就需要⽤之前需要对数组的长度取模运算,得到的余数才能用来访问数组下标。

源码中模运算就是把散列值和数组长度 - 1 做⼀个 " 与& " 操作,位运算⽐取余 % 运算要快。

这也正好解释了为什么 HashMap 的数组长度要取 2 的整数幂。因为这样(数组长度 - 1)正好相当于⼀个 “低位掩码”。 与 操作的结果就是散列值的⾼位全部归零,只保留低位值,用来做数组下标访问。以初始长度 16 为例,16-1=15。2 进制表⽰是 0000 0000 0000 0000 0000 0000 0000 1111 。和某个散列值做 与 操作如下,结果就是截取了最低的四位值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值