我们都知道,hashMap在实现的时候,为了寻找在数组上的位置,主要做了两件事

int hash = hash(key);
int i = indexFor(key, table.length);

这个时候得到i才是数组上的位置。

这两个方法详解如下

JDK8对扰动函数的修改,只进行了一次移位(又移16bit),再和key.hashCode()做异或,如图

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

这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来访问数组下标。源码中模运算是在这个indexFor( )函数里完成的。

bucketIndex = indexFor(int h, table.length);

其中IndexFor代码

 static int indexFor(int h, int length){
return h & (length - 1);
}

indexFor代码,正好解释了为什么HashMap的数组长度要取2的整次幂。因为这样(数组长度-1)正好相当于一个“低位掩码”。“与”操作的结果就是散列值的高位全部归零,只保留低位值,用来做数组下标访问。以初始长度16为例,16-1=15。2进制表示是00000000 00000000 00001111。和某散列值做“与”操作如下,结果就是截取了最低的四位值。

但这时候问题就来了,这样就算我的散列值分布再松散,要是只取最后几位的话,碰撞也会很严重。更要命的是如果散列本身做得不好,分布上成等差数列的漏洞,恰好使最后几个低位呈现规律性重复,就无比蛋疼。

这时候“扰动函数”的价值就体现出来了,说到这里大家应该猜出来了。看下面这个图,

右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。

JDK 7做了4次右移,估计是边际效应的原因,JDK8就只做了一次右移。

另外 JDK8在链表长度超过8的时候,就使用红黑树做存储。这一改变大大优化了很多性能。

HashMap之扰动函数和低位掩码的更多相关文章

  1. 关于 HashMap 随笔

    hashMap 的一些认识: 基于哈希表的Map接口的非同步实现,定义了键映射到值的规则 此实现提供所有可选的映射操作,并允许使用null值和null键 根据hash算法,确定key-value的存贮 ...

  2. HashMap是如何工作的

    目录 1 HashMap在JAVA中的怎么工作的? 2 什么是哈希? 3 HashMap 中的 Node 类 4 键值对在 HashMap 中是如何存储的 5 哈希碰撞及其处理 6 HashMap 的 ...

  3. HashMap中的hash函数

    在写一个HashSet时候有个需求,是判断HashSet中是否已经存在对象,存在则取出,不存在则add添加.HashSet也是通过HashMap实现,只用了HashMap的key,value都存储一个 ...

  4. 学习HashMap的笔记

    对于HashMap只是学习了下put,remove方法,hashMap是数组+链表+红黑树组成 所以下面贴出我自己给代码的注释,看不懂的见谅哈,毕竟我也是刚了解,如果有错误的地方请指出,非常感谢 pu ...

  5. 2、JDK8中的HashMap实现原理及源码分析

    本篇提纲.png 本篇所述源码基于JDK1.8.0_121 在写上一篇线性表的文章的时候,笔者看的是Android源码中support24中的Java代码,当时发现这个ArrayList和Linked ...

  6. 【JDK1.8】Java HashMap实现细节

    底层是用数组实现的 /** * The table, initialized on first use, and resized as * necessary. When allocated, len ...

  7. Java源码系列2——HashMap

    HashMap 的源码很多也很复杂,本文只是摘取简单常用的部分代码进行分析.能力有限,欢迎指正. HASH 值的计算 前置知识--位运算 按位异或操作符^:1^1=0, 0^0=0, 1^0=0, 值 ...

  8. JDK8中的HashMap实现原理及源码分析

    大纲 一.什么是Hash?什么是HashMap? 二.HashMap的内部实现机制 1.HashMap基本元素 ①DEFAULT_INITIAL_CAPACITY&MAXIMUM_CAPACI ...

  9. Java容器:HashMap连环炮

    本文来源于:https://mp.weixin.qq.com/s/oRx-8XXbgage9Hf97WrDQQ, 公众号:安琪拉的博客 前言 HashMap应该算是Java后端工程师面试的必问题,因为 ...

随机推荐

  1. C# 委托和事件 实现窗体间的通信

    例子 : 点击form1上的按钮打开form2窗口,在form2窗体中的文本框中输入一个值后,在点击form2窗体中按钮,在form2中的文本框中输入的值也会在form1中的文本框中出现. form1 ...

  2. latex算法步骤如何去掉序号

    想去掉latex算法步骤前面的序号,如下 我想去掉每个算法步骤前面的数字序号,1,2,3,因为我已经写了step.我们只需要引用a lgorithmic这个包就可以了,代码如下: \usepackag ...

  3. 通过busybox制作根文件系统

    通过busybox制作根文件系统可以自定义选项,在制作的根文件系统中添加需要的命令,指定生成的根文件系统到相应的目录下. 一. 根文件系统的获取方式--->官网: https://busybox ...

  4. python内存分析

    安装 首先安装memory_profiler和psutil pip install memory_profiler pip install psutil 在需要分析的函数前面添加装饰器@profile ...

  5. java程序员必知的 8大排序

    Java常用的八种排序算法与代码实现 排序问题一直是程序员工作与面试的重点,今天特意整理研究下与大家共勉!这里列出8种常见的经典排序,基本涵盖了所有的排序算法. 1.直接插入排序 我们经常会到这样一类 ...

  6. js中关键字 const , let , var 的用法区别

    1.const定义的变量不可以修改,而且必须初始化. 2.var定义的变量可以修改,如果不初始化会输出undefined,不会报错. 3.let是块级作用域,函数内部使用let定义后,对函数外部无影响 ...

  7. Nginx-常用命令和配置文件

    Nginx常用命令 1.启动命令 在/usr/local/nginx/sbin 目录下执行 ./nginx 2.关闭命令 在/usr/local/nginx/sbin 目录下执行 ./nginx s ...

  8. 【leetcode】1026. Maximum Difference Between Node and Ancestor

    题目如下: Given the root of a binary tree, find the maximum value V for which there exists different nod ...

  9. 【leetcode】447. Number of Boomerangs

    题目如下: 解题思路:我首先用来时间复杂度是O(n^3)的解法,会判定为超时:后来尝试O(n^2)的解法,可以被AC.对于任意一个点,我们都可以计算出它与其余点的距离,使用一个字典保存每个距离的点的数 ...

  10. Check the port occupy on Mac OSX

    Check the port occupy on Mac OSX lsof -i :7070 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME si ...