在写一个HashSet时候有个需求,是判断HashSet中是否已经存在对象,存在则取出,不存在则add添加。HashSet也是通过HashMap实现,只用了HashMap的key,value都存储一个赘余的Object,如下是HashSet中持有的HashMap对象,add函数:

  private transient HashMap<E,Object> map;// Dummy value to associate with an Object in the backing Mapprivate static final Object PRESENT = new Object(); 
public boolean add(E e) {return map.put(e, PRESENT)==null;
}

中在HashMap中的hash函数判断key是否存在,如下图所示:

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

在看到这段代码时疑问产生了,为什么hash函数这么设计?查过资料之后解释如下(如下内容来自网络-知乎胖胖的答案):

这段代码叫“扰动函数”

大家都知道上面代码里的key.hashCode()函数调用的是key键值类型自带的哈希函数,返回int型散列值。

理论上散列值是一个int型,如果直接拿散列值作为下标访问HashMap主数组的话,考虑到2进制32位带符号的int表值范围从-21474836482147483648。前后加起来大概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次的话,多了可能边际效用也不大,所谓为了效率考虑就改成一次了。

转载于:https://www.cnblogs.com/zhengwang/p/8136164.html

HashMap中的hash函数相关推荐

  1. HashMap中的hash与rehash

    HashMap中的hash与rehash 我们知道HashMap中经常用到hash()方法. 比如:put()方法中 public V put(K key, V value){if(key==null ...

  2. HashMap中的Hash码怎么计算,为什么要这样做?

    HashMap面试系列(1) Q:HashMap中的Hash码如何计算?为什么要这么做? 注意:HashCode()是对象的Hash码,和HashMap中的Hash码不是同一个东西 1.8版本如下 s ...

  3. Zcash中的hash函数

    1. 引言 Zcash中的hash函数主要有: BLAKE2 Hash Function Group Hash into Jubjub Pedersen Hash Function Mixing Pe ...

  4. HashMap中的hash算法的几个思考

    HashMap中哈希算法的关键代码 //重新计算哈希值 static final int hash(Object key) {int h;return (key == null) ? 0 : (h = ...

  5. hashmap中的hash扰动函数

    https://www.zhihu.com/question/20733617 转载于:https://www.cnblogs.com/lushilin/p/6142597.html

  6. hash算法_阿里面试官:讲一下Hashmap中hash算法!

    注:本文内容全部基于jdk8讲述. 相信很多人都知道,在JDK8中,HashMap的容量总是2的n次幂,那么这么设计的目的究竟是什么呢?我可不可以将默认的初始容量从16改成20呢,扩容的时候我可不可以 ...

  7. 全网把Map中的hash()分析的最透彻的文章,别无二家。

    你知道HashMap中hash方法的具体实现吗?你知道HashTable.ConcurrentHashMap中hash方法的实现以及原因吗?你知道为什么要这么实现吗?你知道为什么JDK 7和JDK 8 ...

  8. bat从数组中找出相同数字并删除_全网把Map中的hash()分析的最透彻的文章,别无二家...

    原文地址:https://mp.weixin.qq.com/s/qCHkzs4JPOipB-ZzqrfbeQ 作者: Hollis 你知道HashMap中hash方法的具体实现吗? 你知道HashTa ...

  9. Hash函数及其应用

    本文部分内容摘自网络,参考资料链接会在文后给出,在此感谢原作者的分享. 计算理论中,没有Hash函数的说法,只有单向函数的说法.所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数 ...

最新文章

  1. linux普通高速缓存,linux页高速缓存
  2. Linux Shell 命令--tr
  3. python流程控制-Python流程控制
  4. syslog记录history历史记录
  5. 第四届泉水文化论坛协调会-商协社团:平台经济谋定水产业
  6. Python--粒子滤波定位案例程序
  7. 十六、PHP框架Laravel学习笔记——构造器的增删改
  8. 第六篇:如何学习C语言?
  9. 外观模式 门面模式 Facade 结构型 设计模式(十三)
  10. 面试官、女朋友都满意系列 - 决策树
  11. 解决Oracle11g密码180天过期,账号锁住的问题
  12. python optimize_SciPy优化optimize模块用法
  13. JAVAWeb汽车销售管理系统
  14. c++使用librdkafka kerberos认证
  15. 关于雷霄骅博士的博客FFMPEG+SDL的音频播放器播放有杂音的问题
  16. window10耳机插入没有声音,电脑里没有Realtek 高清晰音频管理器解决方法
  17. stata输出相关系数表到word
  18. Ueditor基本用法-kityformula-上传图片-手写公式myscript
  19. 强大的dex反编译器
  20. console接口是干嘛的(console接口是干嘛的电信室外光猫)

热门文章

  1. CSDDN特约专稿:个性化推荐技术漫谈
  2. 概述HTML文档的基本结构,HTML概述与基本结构
  3. e1载波把32个信道按_E1载波把32个信道按(11)方式复用在一条2.048Mb/s的高速信道上,每条话音信道的数据速率是(12)。...
  4. 如何计算_平整场地如何计算?
  5. pythonapriori算法特点_Python --深入浅出Apriori关联分析算法(一)
  6. python抓取简单网页_【Python3 爬虫】01_简单页面抓取
  7. java odbc dbf,什么是Java的最佳开源dbf驱动程序?
  8. python 分数序列求和公式_Python分数序列求和,编程练习题实例二十四
  9. wxpython多个面板_wxpython:隐藏其中一个拆分窗口面板
  10. python2 python3编码_Python2和Python3编码问题-从底层出发