下面是HashTable源码中的put方法:

注意上面注释标注的地方:

HashTable对于元素在哈希表中的坐标算法是:

  1. 将对象自身的哈希值key.hashCode()变为正数:hash & 0x7FFFFFFF
  2. 将上面得到的哈希值对表长取余,映射到哈希表中去。

HashMap中哈希算法比HashTable中的稍微复杂一点。总体可以分为两步:

一、重新计算key本身的哈希值

上面代码中,首先是一个三目运算符,判断key是不是等于null,等于null,则返回0作为哈希值。否则,运算(h=key.hashCode()) ^ (h >>> 16),将key的哈希值的高位与低位异或的结果作为低位,改为不变。

‘>>>’是无符号右移操作,高位补0.

为什么要这么做呢?下面这一点讲了过后我们就明白了

二、哈希坐标的计算

同样以put方法为例

从最后一行我们可以看出,HashMap的哈希坐标计算方法是: (n - 1) & hash,其中hash就是我们第一点讲的改进哈希码。

HashMap为什么要使用改进的hash码?

举例分析如下,假设key的原始哈希值是’1111 1111 1111 1111 1111 0000 1110 1010’


(ps:图片来自:https://blog.csdn.net/john_520/article/details/57415084)

我们注意到,在上面这种哈希表长度较小的情况下,哈希码只有低4位与表的长度进行了关联性计算。这会造成哈希码的不充分使用,从而更容易引起哈希冲突。为了充分利用哈希码的高位,HashMap通过(h=key.hashCode())
^ (h >>> 16)运算,将高位与低位异或,使得即使在表长较小的情况下,高位也能参与计算,使得冲突的概率减小了。

HashMap为什么要使用 (n - 1)
& hash ,而不是HashTable中求模的方式来计算哈希坐标呢?

我在Stack Overflow上找到了一个解答:

意思是说:“规范的解决方法是将哈希值与表长取模,而这个方式((n - 1) & hash
武汉英语学校充分利用了HashMap的表长是2的整数次幂的事实,使用效率较高的位与运算(取模的高度优化)来替代昂贵的取模运算。”

实际上这两种方式的实质是一样的,它只是利用了这样一个事实:在n是2的幂的情况下,(n - 1) & hash 等同于 hash%h。

在n是2的幂的情况下,为什么 (n - 1) & hash等同于
hash%h?

我们以10为例,演示如下:

可以看到,两种方式的计算结果是相同的(这不是巧合),这实际上是一个数学规律。

HashMap是怎么使得表长始终为2的整数次幂的?

在源码中有这样一个方法:

这个方法的作用如注释所说,是求大于cap的最小2的整数次幂。在用户指定的初始容量不是2的幂时,HashMap会调用该方法将其变得符合要求。此后,每次扩容时是这样的:

直接使用oldCap<<1来将容量扩大为原来的2倍,即乘以21

  1. 对数学规律的恰当应用可以优化代码的运行效率
  2. 位运算在JDK中运用的十分广泛,如上面讲解的使用位“与”运算替代求模。这种替代的内在原因是位运算比数学运算快很多。优化都在细节处。

我对JDK源码的阅读和中文注释都已经同步到Github,欢迎英语阅读困难户前往查看:)
链接是:https://github.com/Dodozhou/JDK,喜欢的话别忘了star哦。

HashMap与HashTable的哈希算法——JDK1.9源码阅读总结的更多相关文章

  1. 13 HashTable抽象哈希表类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  2. JDK1.8源码阅读笔记(1)Object类

    JDK1.8源码阅读笔记(1)Object类 ​ Object 类属于 java.lang 包,此包下的所有类在使⽤时⽆需⼿动导⼊,系统会在程序编译期间⾃动 导⼊.Object 类是所有类的基类,当⼀ ...

  3. JDK1.8源码阅读系列之三:Vector

    本篇随笔主要描述的是我阅读 Vector 源码期间的对于 Vector 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出- 先来看一下 Vector 的继承图: 可以看出,Vector 的直 ...

  4. JDK1.8源码阅读笔记(2) AtomicInteger AtomicLong AtomicBoolean原子类

    JDK1.8源码阅读笔记(2) AtomicInteger AtomicLong AtomicBoolean原子类 Unsafe Java中无法直接操作一块内存区域,不能像C++中那样可以自己申请内存 ...

  5. JDK1.8源码阅读系列之四:HashMap (原创)

    本篇随笔主要描述的是我阅读 HashMap 源码期间的对于 HashMap 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出- 接下来会从以下几个方面介绍 HashMap 源码相关知识: 1 ...

  6. JDK1.7源码阅读tools包之------ArrayList,LinkedList,HashMap,TreeMap

    1.HashMap 特点:基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 Has ...

  7. JDK1.8源码阅读系列之一:ArrayList

    本篇随笔主要描述的是我阅读 ArrayList 源码期间的对于 ArrayList 的一些实现上的个人理解,有不对的地方,请指出- 先来看一下 ArrayList 的继承图: 由图可以看出,Array ...

  8. JDK1.8源码阅读系列之二:LinkedList

    本篇随笔主要描述的是我阅读 LinkedList 源码期间的对于 LinkedList 的一些实现上的个人理解,有不对的地方,请指出- 先来看一下 LinkedList 的继承图: 由于 Abstra ...

  9. jdk1.8源码阅读

    一.java.lang java的基础类 1.object 所有类的爸爸 registerNatives() Class<?> getClass():返回运行时的类 int hashCod ...

随机推荐

  1. TensorFlow学习笔记11-开始用TensorFlow

    TensorFlow运作方式 要用到的代码都在Github上.当然,如果你本地装了TensorFlow,也可以用Everything直接搜索以下文件: mnist.py fully_connected ...

  2. scrapy爬取booking酒店评论数据

    # scrapy爬取酒店评论数据 -- 代码 here:github地址:https://github.com/760730895/scrapy_Booking--  采用scrapy爬取酒店评论数据 ...

  3. jsp+servlet的简单实现

    开发环境 tomcat7.0,MyEclipse 10 1.建一个简单的Web Project ,项目名jspServlet: 2.在src目录下建一个package ,为com.fandy.serv ...

  4. HDFS启动过程概述及集群安全模式操作

    1.启动过程概述 Namenode启动时,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作.一旦在内存中成功建立文件系统元数据的映像,则创建一个新的fsimage文件 ...

  5. JavaScript之BOM操作

    一, 什么是BOM BOM:Browser Object Model,浏览器对象模型 BOM的结构图: 从上图也可以看出: window对象是BOM的顶层(核心)对象,所有对象都是通过它延伸出来的,也 ...

  6. python学习第四十六天dir( )函数用法

    dir( )函数有点像目录的意思,但是他是包含由模块定义的名称的字符串的排序列表.这个列表包含模块中定义的所有模块,变量和函数的名称. 列举其用法 import time content = dir( ...

  7. Python pass是空语句用法

    在条件判断,还是函数中,有时候不需要输出任何东西,也不能留空,python提供空的语句,下面讲述pass空语句的用法 1,关键词 pass 2,用法 for letter in 'Python': i ...

  8. 判断当前终端是手机还是pc端并进行不同的页面跳转

    判断当前设备(终端)是手机还是pc端并进行不同的页面跳转 DEMO 1 <script type="text/javascript"> function browser ...

  9. HDU_2007

    /** *注意:输入的两个数字的大小并不确定 */ #include <iostream> #include <stdio.h> #include <string.h&g ...

  10. MySQL Server类型的MySQL 客户端的下载、安装和使用

    mysql server 5.5的下载 下载地址:https://dev.mysql.com/downloads/mysql/5.0.html#linux mysql server 5.5的安装 双击 ...