【JDK1.8】Java HashMap实现细节
底层是用数组实现的
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
主要介绍一下Java 8源码中的HashMap中的hash原理,先看代码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Java 7中是这样的
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
上面这段代码其实叫做"扰动函数"
下面摘自https://www.zhihu.com/question/20733617
大家都知道上面代码里的key.hashCode()函数调用的是key键值类型自带的哈希函数,返回int型散列值。
理论上散列值是一个int型,如果直接拿散列值作为下标访问HashMap主数组的话,考虑到2进制32位带符号的int表值范围从-2147483648到2147483648。前后加起来大概40亿的映射空间。只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。
但问题是一个40亿长度的数组,内存是放不下的。你想,HashMap扩容之前的数组初始大小才16。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来访问数组下标。源码中模运算是在这个indexFor( )函数里完成的。
bucketIndex = indexFor(hash, table.length);
indexFor的代码也很简单,就是把散列值和数组长度做一个"与"操作,
static int indexFor(int h, int length) {
return h & (length-1);
}
顺便说一下,这也正好解释了为什么HashMap的数组长度要取2的整次幂。因为这样(数组长度-1)正好相当于一个“低位掩码”。“与”操作的结果就是散列值的高位全部归零,只保留低位值,用来做数组下标访问。以初始长度16为例,16-1=15。2进制表示是00000000 00000000 00001111。和某散列值做“与”操作如下,结果就是截取了最低的四位值。
10100101 11000100 00100101
& 00000000 00000000 00001111
----------------------------------
00000000 00000000 00000101 //高位全部归零,只保留末四位
但这时候问题就来了,这样就算我的散列值分布再松散,要是只取最后几位的话,碰撞也会很严重。更要命的是如果散列本身做得不好,分布上成等差数列的漏洞,恰好使最后几个低位呈现规律性重复,就无比蛋疼。
这时候“扰动函数”的价值就体现出来了,说到这里大家应该猜出来了。看下面这个图,
右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。
最后我们来看一下Peter Lawley的一篇专栏文章《An introduction to optimising a hashing strategy》里的的一个实验:他随机选取了352个字符串,在他们散列值完全没有冲突的前提下,对它们做低位掩码,取数组下标。
结果显示,当HashMap数组长度为512的时候,也就是用掩码取低9位的时候,在没有扰动函数的情况下,发生了103次碰撞,接近30%。而在使用了扰动函数之后只有92次碰撞。碰撞减少了将近10%。看来扰动函数确实还是有功效的。
但明显Java 8觉得扰动做一次就够了,做4次的话,多了可能边际效用也不大,所谓为了效率考虑就改成一次了。
【JDK1.8】Java HashMap实现细节的更多相关文章
- JDK1.7中HashMap底层实现原理
一.数据结构 HashMap中的数据结构是数组+单链表的组合,以键值对(key-value)的形式存储元素的,通过put()和get()方法储存和获取对象. (方块表示Entry对象,横排表示数组ta ...
- 【转】Java HashMap的死循环
问题的症状 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问题,所以需要变成多线程的,于是,变成多线程后到了线上,发现 ...
- Java - HashMap 多线程安全解析
HashMap多线程并发问题分析 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问 ...
- Java总结——常见Java集合实现细节(1)
Java提高——常见Java集合实现细节(1) 2018年04月18日 15:07:35 阅读数:25 集合关系图 Set和Map set代表一种集合元素无序.集合元素不可重复的集合 map代表一种由 ...
- 十个问题带你了解和掌握java HashMap
十个问题带你了解和掌握java HashMap 一.前言 本篇内容是源于 " 由阿里巴巴Java开发规约HashMap条目引发的故事",并在此基础上加了自己的对HashMap更多的 ...
- java HashMap源码分析(JDK8)
这两天在复习JAVA的知识点,想更深层次的了解一下JAVA,所以就看了看JAVA的源码,把自己的分析写在这里,也当做是笔记吧,方便记忆.写的不对的地方也请大家多多指教. JDK1.6中HashMap采 ...
- Java:HashMap原理与设计缘由
前言 Java中使用最多的数据结构基本就是ArrayList和HashMap,HashMap的原理也常常出现在各种面试题中,本文就HashMap的设计与设计缘由作出一一讲解,并解答面试常见的一些问题. ...
- Java——HashMap底层源码分析
1.简介 HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的. HashMap 最多只允许一条记录的key为 nu ...
- Java——HashMap集合详解
第一章 HashMap集合简介 1.1 介绍 HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对.HashMap 的实现不是同步的,这意味着它不是线程安 ...
随机推荐
- 二、sql新增后返回主键|sql 使用 FOR XML PATH实现字符串拼接|sql如果存在就修改不存在就新增
一.sql新增后返回主键 1,返回自增的主键: INSERT INTO 表名 (字段名1,字段名2,字段名3,…) VALUES (值1,值2,值3,…) SELECT @@IDENTITY 2,返回 ...
- 愚蠢的sql语法错误(sum (xxx))
sum和()之间打了一个空格,导致一致报sql语法错误,看了半天不知道怎么回事orz
- asp.net相关的一些代码
显示目录下的内容 using System.IO; DirectoryInfo di = new DirectoryInfo(Server.MapPath("Views/video" ...
- ubuntu安装samba
1, sudo apt-get install samba sudo apt-get install smbclient 2, vi /etc/samba/smb.conf 在最后加字段(为可读可 ...
- Jetson Nano系列教程3:GPIO
摘要: JetsonTX1,TX2,AGXXavier和Nano开发板包含一个40引脚的GPIO头,类似于Raspberry PI中的40引脚头.这些GPO可以通过JetsonGPIOLibrary包 ...
- 使用ui给定的字体,通过css引入字体库
@font-face { font-family: 'LCDMonoNormal'; src: url('../../assets/fonts/LCDM2N__.eot'); src: url('.. ...
- [CSP-S模拟测试41]题解
中间咕的几次考试就先咕着吧…… A.夜莺与玫瑰 枚举斜率.只考虑斜率为正且不平行于坐标轴的直线,最后把$ans\times 2$再$+1$即可. 首先肯定需要用$gcd(i,j)==1$确保斜率的唯一 ...
- 学号 20175223 《Java程序设计》第10周学习总结
目录 教材学习内容总结 代码调试中的问题和解决过程 1. XAMPP无法启用 MySQL 程序. 2. Ubuntu 无法下载或更新. [代码托管] 学习进度条 参考资料 目录 教材学习内容总结 第十 ...
- 尚学linux课程---12、vim操作命令2
尚学linux课程---12.vim操作命令2 一.总结 一句话总结: 要看不同的视频,每个视频的关键点都不一样,不如之间的的视频就没讲到vim中set nu是什么意思 学了的内容一定要练,不然真的是 ...
- Callable创建线程
(1)Callable接口更像是Runnable接口的增强版,相比较Runable接口,Call()方法新增捕获和抛出异常的功能;Call()方法可以返回值<br> (2)Future接口 ...