深刻理解HashMap(二): 关键源码逐行分析之hash算法

前言

系列文章目录java

前面咱们讨论了HashMap的结构, 接下来几篇咱们从源码角度来看HashMap的实现细节.算法

本篇咱们就来聊聊HashMap的hash算法segmentfault

本文的源码基于 jdk8 版本.数组

hash算法

上一篇文章咱们提到, 为了利用数组索引进行快速查找, 咱们须要先将 key值映射成数组下标. 由于数组的下标是有限的集合, 因此咱们能够先经过hash算法将key映射成整数, 再将整数映射成有限的数组下标:函数

Object -> int -> index

对于 Object -> int 部分, 使用的就是hash function, 而对于 int -> index 部分, 咱们能够简单的使用对数组大小取模来实现.性能

下面咱们就来看看HashMap使用了什么hash算法.code

首先咱们来看维基百科对于hash function的定义:对象

散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中建立小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,从新建立一个叫作散列值(hash values,hash codes,hash sums,或hashes)的指纹。

在java中, hash函数是一个native方法, 这个定义在Object类中, 因此全部的对象都会继承.继承

public native int hashCode();

由于这是一个本地方法, 因此咱们没法看到它的具体实现, 可是从函数签名上能够看出, 该方法将任意对象映射成一个整型值.调用该方法, 咱们就完成了 Object -> int的映射索引

因此将 key映射成index 的方式能够是

key.hashCode() % table.length

那么HashMap是这样作的吗? 事实上, HashMap定义了本身的散列方法:

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

咱们知道, int类型是32位的, h ^ h >>> 16 其实就是将hashCode的高16位和低16位进行异或, 这充分利用了高半位和低半位的信息, 对低位进行了扰动, 目的是为了使该hashCode映射成数组下标时能够更均匀, 详细的解释能够参考这里.

另外, 从这个函数中, 咱们还能够获得一个意外收获:

HashMap中key值能够为null, 且null值必定存储在数组的第一个位置.

性能提高

前面咱们提到, 将hash值转换成数组下标咱们能够采用取模运算, 可是取模运算是十分耗时的.

另外一方面, 咱们知道, 当一个数是 2^n 时, 任意整数对2^n取模等效于:

h % 2^n = h & (2^n -1)

这样咱们就将取模操做转换成了位操做, 而位操做的速度远远快于取模操做.

为此, HashMap中, table的大小都是2的n次方, 即便你在构造函数中指定了table的大小, HashMap也会将该值扩大为距离它最近的2的整数次幂的值. 这在咱们下面分析构造函数的时候就能看到了.

(完)

下一篇 : 深刻理解HashMap(三): 关键源码逐行分析之构造函数

查看更多系列文章:系列文章目录

相关文章
相关标签/搜索