iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >深入探究HashMap二次Hash原因
  • 345
分享到

深入探究HashMap二次Hash原因

HashMap二次HashJavaHashMapHash 2023-01-04 12:01:09 345人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录1. 前言2. 哈希码的作用3. 二次Hash4. 为啥右移16位5. 总结1. 前言 HashMap对于Java程序员来说一定不陌生,除了平时开发会经常使用外,它也是面试官非常

1. 前言

HashMap对于Java程序员来说一定不陌生,除了平时开发会经常使用外,它也是面试官非常喜欢问的一个知识点。HashMap是哈希表的一个经典实现,底层数据结构数组+链表,在jdk8中还引入了红黑树,以解决链表线性查找的效率问题。HashMap设计的非常优秀,源码两千多行,有很多可以拿出来讨论的点,本篇文章主要分析HashMap二次哈希的目的。

2. 哈希码的作用

首先,我们得先了解哈希码的作用是什么?HashMap底层采用数组+链表/红黑树的数据结构来存储键值对的映射关系,数组就是若干个哈希槽Solt,HashMap会通过Key算出的哈希码来计算下标Index,Index决定了键值对应该落在哪个槽里。不同的哈希码算出相同的下标Index,就会导致哈希碰撞,一旦发生哈希碰撞,HashMap的查找效率就会从O(1)退化成O(n)或者O(logn)。所以,一个好的哈希函数应该要尽可能的分散,否则就会影响到HashMap的效率。

3. 二次Hash

我们已经知道,HashMap会根据哈希码计算下标,哈希码的分散性越好,HashMap的效率也就越高。我们先看一下HashMap计算下标的过程,就知道它为啥要做二次Hash了。

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

上面是HashMap根据二次Hash计算出的哈希码,计算键值对下标的代码,length是底层数组的长度。HashMap采用了位运算,而非我们常见的取模运算,这里你可以先略过,它俩的效果是一样的。

我们先来看看如果不做二次Hash的情况下,会出现什么问题。现在,我假设数组长度为16,那么当哈希码为5时,下标Index结果是5。

 00000000000000000000000000000101
&00000000000000000000000000001111
=00000000000000000000000000000101
=5

当哈希码为65541时,下标Index结果依然是5,不同的哈希码算出相同的下标,哈希碰撞了。

 00000000000000010000000000000101
&00000000000000000000000000001111
=00000000000000000000000000001101
=5

从这个与运算的过程,大家肯定也都发现了,就是哈希码的高位压根就没有参与运算,全部被丢弃了。不管哈希码的高位是多少,都不会影响最终Index的计算结果,因为只有低位才参与了运算,这样的哈希函数我们认为是不好的,它会带来更多的冲突,影响HashMap的效率。

如何解决这个问题呢?最简单的办法就是让高位也参与到运算,高位不一样也会导致最终的Index结果不一样,减少哈希碰撞的概率。事实上,HashMap也就是这么做的,下面是HashMap做二次Hash的源码:

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

HashMap通过将哈希码的高16位与低16位进行异或运算,得到一个新的哈希码,这样就可以让高位也参与到运算,这个函数也被称作「扰动函数」。

我们用同样的哈希码,来看看经过二次Hash后的哈希码,是否会带来不一样的效果。
仍然假设数组长度为16,那么当哈希码为5时,下标Index是5,结果不变。

 0000000000000101
^0000000000000000
=0000000000000101

 00000000000000000000000000000101
&00000000000000000000000000001111
=00000000000000000000000000000101
=5

当哈希码为65541时,下标Index结果是4,竟然没有发生哈希碰撞。

 0000000000000101
^0000000000000001
=0000000000000100

 00000000000000010000000000000100
&00000000000000000000000000001111
=00000000000000000000000000000100
=4

可以看到,HashMap通过加入一个扰动函数,让原本会发生碰撞的两个哈希码,不再冲突。

4. 为啥右移16位

HashMap的扰动函数,是拿高16位和低16位做异或运算,把高位的特征和地位的特征组合起来,以此来降低哈希碰撞的概率。为啥是16位?而不是8位或24位或其它位?

根据哈希码计算下标Index的过程,大家也发现了。实际上,只有数组长度以内的低位才会参与运算。例如数组长度是16,那么只有低4位会参与计算;如果数组长度是256,那么只有低8位会参与计算;如果数组长度是65536,那么只有低16位会参与计算。HashMap取16位是一个折中的数字,绝大部分情况下,HashMap数组的长度都不会超过65536。

5. 总结

HashMap底层采用数组+链表/红黑树来存储键值对,会根据Key的哈希码来计算键值对落在数组的哪个下标。如果不同的哈希码算出相同的下标,就会导致哈希碰撞,影响HashMap的性能。HashMap要做的,就是尽量避免哈希碰撞,所以加入了扰动函数。扰动函数会将哈希码的高16位与低16位做异或运算,让高位也参与到下标的计算过程中来,从而影响最终下标的计算结果,减少哈希碰撞的概率。至于为啥是16位,这是因为哪些位会参与到下标的计算,取决于HashMap数组的长度,在绝大部分情况下,数组的长度都不会超过65536,16位是一个折中的数字。

到此这篇关于深入探究HashMap二次Hash原因的文章就介绍到这了,更多相关HashMap二次Hash内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 深入探究HashMap二次Hash原因

本文链接: https://www.lsjlt.com/news/176690.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作