HashMap 的 hash 方法原理
来源:互联网 发布:消费资本论是传销 知乎 编辑:程序博客网 时间:2024/06/06 18:23
近来碰到一个需求要重写hashmap,研究了一下hashmap的源码,发现在hash查找的地方有一些巧秒的处理,在这里记录一下。
java7 的 HashMap 中使用了一个 hash 方法来做 bit shifting,在注释中说明是为了防止一些实现比较差的hashCode() 方法.
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);
}
java8中这步已经简化了,只做一次16位右位移异或混合,而不是四次,但原理是不变的。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); //key.hashCode()为哈希算法,返回初始哈希值
}
这段代码叫“扰动函数”。代码里的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 //高位全部归零,只保留末四位
问题来了,这样就算我的散列值分布再松散,要是只取最后几位的话,碰撞也会很严重。更要命的是如果散列本身做得不好,分布上成等差数列的漏洞,恰好使最后几个低位呈现规律性重复.这时侯扰动函数hash()的用处就体现出来了。
h=hashcode():1111 1111 1111 1111 1111 0000 1110 1010 调用hashCode()
----------------------------------------------------------------------------------------------------------------------------------------
h:1111 1111 1111 1111 1111 0000 1110 1010
h>>>16:0000 0000 0000 0000 1111 1111 1111 1111 计算hash
hash=h^(h>>>16):1111 1111 1111 1111 0000 1111 0001 0101
----------------------------------------------------------------------------------------------------------------------------------------
(n-1)&hash:0000 0000 0000 0000 0000 0000 0000 1111
1111 1111 1111 1111 0000 1111 0001 0101 计算haspmap entry数组下标
----------------------------------------------------------------------------------------------------------------------------------------
0101 = 5 下标
右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。
- HashMap 的 hash 方法原理
- JDK 源码中 HashMap 的 hash 方法原理
- java集合之hashMap的hash原理
- Hash原理与HashMap
- HashMap解决hash冲突的方法
- HashMap里的hash、indexFor方法
- 链表法HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- HashMap解决Hash冲突的方法
- HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- hashmap的hash方法出现原因
- HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- HashMap解决hash冲突的方法
- linux启动停止tomcat
- 三级联动
- joseph_lee,几篇好文章
- 当俄罗斯方块遇上2048
- max_wrong
- HashMap 的 hash 方法原理
- table合并单元格 colspan(跨列)和rowspan(跨行)
- redis事务
- JavaScript小特效---全选功能的实现
- Java 实现导出excel表 POI
- 测试seetaface人脸识别引擎
- Request.Params,Request.QueryString,Request.Form,Request.Request.Params的区别
- 如何让你的web应用运行在80端口
- 判断两个单链表是否相交