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 ...
随机推荐
- victoriaMetrics中的一些Sao操作
victoriaMetrics中的一些Sao操作 快速获取当前时间 victoriaMetrics中有一个fasttime库,用于快速获取当前的Unix时间,实现其实挺简单,就是在后台使用一个goro ...
- 关于Spring-JDBC测试类的简单封装
关于Spring-JDBC测试类的简单封装 1.简单封装 /** * Created with IntelliJ IDEA. * * @Author: Suhai * @Date: 2022/04/0 ...
- [题解] XOR Problem
题目大意 对于一个整数序列 \(a_{0...5}\),我们定义它的价值为: \(f(a)=max(|a_0-a_3|,|a_1-a_4|,|a_2-a_5|)\oplus a_0 \oplus a_ ...
- Windows IDEA Community 报错
运行时报错 "CreateProcess error=206,文件名或扩展名太长" 解决方法:https://plugins.gradle.org/plugin/ua.eshepe ...
- 使用VUE+SpringBoot+EasyExcel 整合导入导出数据
使用VUE+SpringBoot+EasyExcel 整合导入导出数据 创建一个普通的maven项目即可 项目目录结构 1 前端 存放在resources/static 下 index.html &l ...
- 技术分享 | 云原生多模型 NoSQL 概述
作者 朱建平,TEG/云架构平台部/块与表格存储中心副总监.08年加入腾讯后,承担过对象存储.键值存储,先后负责过KV存储-TSSD.对象存储-TFS等多个存储平台. NoSQL 技术和行业背景 No ...
- 安装Speedtest到Python
Speedtest模块可以测试主机的网络带宽大小. 运行环境 系统版本:CentOS Linux release 7.3.1611 (Core) 软件版本:无 硬件要求:无 安装过程 1.安装Spee ...
- 【仿真】Carla介绍与基本使用 [1] (附代码 基础版)
0. 参考与前言 主要介绍无人驾驶的仿真环境CARLA,开源社区维护,以下为相关参考链接: Carla官方文档 建议后续找的时候 先按好版本号,有些功能/api 是新版本里有的 Carla官方gith ...
- ApeForms | WinForm窗体UI美化库(Metro扁平风格)演示与安装
ApeForms系列① 快速上手 @ 目录 ApeForms系列① 快速上手 前言 演示视频 快速上手 安装及使用 Demo下载 联系开发者 加入我们 建议与咨询 前言 ApeForms是一套基于Wi ...
- linux 查询文件命令
jps; 当前服务器中所有的java进程: jps |grep XXX; 查询当前服务器某个进程: locate xxx;查询某个文件的位置: