HashMap中的哈希函数分析
首先我们要知道,在理想情况下的哈希表中,哈希函数生成的哈希值是value在数组中的下标,其范围是分布于负无穷到正无穷的整个实整数轴的。而在现实情况下,是不可能存在这么大的一个数组的。接下来分析HashMap怎么处理:
HashMap的put方法:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
put方法使用的不是Object提供的key.hashcode(),而是hash(key):
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
在key!=0的情况下,进行一下拆解分析:
static final int hash(Object key) {
int h = key.hashCode();
int l = h>>>16;
return h^l;
}
先取Object.hashcode(),是32位;然后右移16位,将低16位丢弃;将hashCode的低16位与高16位进行按位异或运算然后返回。
这就是扰动函数,扰动函数是如何减少冲突的?
由开头的分析,我们知道HashMap是不可能使用直接的哈希值的,因为不可能一个HashMap就要分配无限大(或者2^32次方大)的数组空间。
因此实际上HashMap是将哈希值对当前数组长度取余:
//源码部分截取
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);
看tab[i = (n - 1) & hash]这里,HashMap在数组中的实际下标其实是 (数组长度-1)&hash,其实就是hash%数组长度。
以初始长度16为例,一个哈希值分布于整个实整数轴,取余16之后,必然分布于[0,15]区间范围内,也就无需去分配无限大的数组空间了。
这样做有什么问题呢?
一个好的哈希函数,要做到生成的哈希值足够分散。但是对数组长度取余后,相当于只截取了低位(因为HashMap的容量总是16的整数倍)。
如果一个key的哈希值的低四位是0010,那么在取余16之后,就只剩下0010,也就是十进制2。
哈希函数可能设计得在低位不是那么地随机,那么只保留低位的效果,就相当于完全抛弃了高位的随机性,因此需要这样的扰动函数,将高位与低位进行运算,增强低位的随机性。
在这篇文章中《An introduction to optimising a hashing strategy》,对比发现,采用高位扰动低位的方式进行hash,会使得哈希冲突减少10%。
顺便分析一下为什么HashMap的容量总是2的幂次方
首先HashMap的初始容量是16,随后每当实际容量占到了扩容因子*最大容量后,容量扩大为当前的两倍。因此HashMap的容量总是16*2的幂次方。
之前说得hashcode取余数组长度,只有在数组长度为2的幂次方的情况下,才可以转为(n - 1) & hash的位运算,从而提高运算效率。
HashMap中的哈希函数分析的更多相关文章
- HashMap 中的哈希值计算问题
date: 2020-08-21 16:48:00 updated: 2020-08-21 16:52:00 HashMap 中的哈希值计算问题 1. hash 计算 JDK1.8 HashMap源码 ...
- Java中String的hash函数分析
转载自:http://blog.csdn.net/hengyunabc/article/details/7198533 JDK6的源码: [java] view plaincopy /** * Ret ...
- openstack(liberty): devstack中的iniset/iniget函数分析
这个ini开头的函数在devstack的启动配置中用的非常多,他主要负责.ini文件的配置,这个过程包括对相关ini文件的添加,注释,删除,获取信息,多行信息获取等. 这里主要说的iniset和ini ...
- C++中的内联函数分析
1,本节课学习 C++ 中才引入的新的概念,内联函数: 2,常量与宏回顾: 1,C++ 中的 const 常量可以替代宏常数定义,如: 1,const int A = 3; <==> #d ...
- HashMap在并发下可能出现的问题分析
我们都知道,HashMap在并发环境下使用可能出现问题,但是具体表现,以及为什么出现并发问题,可能并不是所有人都了解,这篇文章记录一下HashMap在多线程环境下可能出现的问题以及如何避免. 在分析H ...
- HashMap的小总结 + 源码分析
一.HashMap的原理 所谓Map,就是关联数组,存的是键值对——key&value. 实现一个简单的Map,你也许会直接用两个LIst,一个存key,一个存value.然后做查询或者get ...
- EPANET中的哈希文件——hash.c
/*-----------------------------------------------------------------------------** hash.c**** Imp ...
- 【C# 集合】Hash哈希函数 |散列函数|摘要算法
希函数定义 哈希函数(英語:Hash function)又称散列函数.散列函数.摘要算法.单向散列函数.散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来.该函数将数据打乱混合,重新 ...
- 2、JDK8中的HashMap实现原理及源码分析
本篇提纲.png 本篇所述源码基于JDK1.8.0_121 在写上一篇线性表的文章的时候,笔者看的是Android源码中support24中的Java代码,当时发现这个ArrayList和Linked ...
随机推荐
- XCTF练习题---WEB---robots
XCTF练习题---WEB---robots flag:cyberpeace{6c4b08933075fc620d16d1157ee07a7e} 解题步骤: 1.观察题目,打开场景 2.打开实验场景, ...
- 【Electron】在 WSL2 中 打包 electron Linux 版本
[Electron]在 WSL2 中 打包 electron Linux 版本. 安装 WSL 我使用的是 Ubuntu 20.04.4 LTS 的版本. 安装 WSL 文档地址:https://do ...
- 深度好文:Linux系统内存知识
点击关注上方"开源Linux", 后台回复"读书",有我为您特别筛选书籍资料~ 相关阅读: 深度好文:Linux文件系统剖析 Linux 内存是后台开发人员,需 ...
- bean的自动装配,使用注解开发,使用java的方式配置Spring
bean的自动装配 自动装配是Spring满足bean依赖一种方式! Spring会在上下文中自动寻找,并自动给bean装配属性! 在Spring中有三种装配的方式 在xml中显示的配置 在java中 ...
- Ansible的参数介绍
安装完成ansible后查看ansible的参数:ansible -h ansible 命令格式:Usage: ansible <host-pattern> [options] ansib ...
- VMware虚拟机中安装Linux操作系统(ubuntu)
一.准备工作: 1.下载VMware虚拟机 下载地址:https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evalua ...
- ABP框架之——数据访问基础架构
大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享阅读心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. 几乎所有的业务应用程序都要适用一种数据库基础架构,用来实现数据访问逻辑,以便从数 ...
- Vben Admin 源码学习:项目初始化
0x00 前言 Vue-Vben-Admin 是一个免费开源的中后台模版.使用了最新的vue3,vite2,TypeScript等主流技术开发,开箱即用的中后台前端解决方案考. 本系列本着学习参考的目 ...
- MTK 虚拟 sensor bring up (pick up) sensor2.0
pick up bring up sensor2.0 1.SCP侧的配置 (1) 放置驱动pickup.c (2) 添加底层驱动文件编译开关 (3) 加入编译文件 (4) 增加数据上报方式 (5)修改 ...
- 【原创】项目一GoldenEye
实战流程 1,通过nmap查找本段IP中存活的机器 ┌──(root㉿whoami)-[/home/whoami/Desktop] └─# nmap -sP 192.168.186.0/24 排查网关 ...