java中对hash的理解

介绍:

在计算机领域中哈希涉及的范围非常广泛,而且是较长使用的一种算法和数据结构,对此我们在后端开发中不断地使用由jdk提供的方法进行使用。由于长时间的使用,很少人会去对里面的核心进行分析和学习。HashMap是通过一个Entry的数组实现的。而Entry的结构有三个属性,key,value,next。如果在c中,我们遇到next想到的必然是指针,其实在java这就是个指针。每次通过hashcode的值,来散列存储数据。今天就来看下那些对于一个专业的开发者而言必要了解的东西。

引用一句百度对于哈希算法的定义:哈希算法可以将任意长度的二进制值引用为较短的且固定长度的二进制值,把这个小的二进制值称为哈希值。

一、HashMap

HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。HashMap数组每一个元素的初始值都是Null。

HashMap的默认初始长度?为什么?
HashMap的初始化长度是16,负载因子是0.75,并且每次自动扩展或是手动初始化时,长度必须是2的幂。之所以选择16,是因为有效提供给key映射到index的Hash算法。从Key映射到HashMap数组的对应位置,会用到一个Hash函数。利用Key的HashCode值来做某种运算来达到一种尽可能均匀分布的Hash函数,这种算法采用的是位运算的方式实现,这也是初始化长度为16的另一个原因,使用16的长度可以比其他相对范围内的数值运算后出现的数更独立,减少了同一个数值出现的次数,实现了更均匀的结果。

如何进行位运算呢?有如下的公式(Length是HashMap的长度):
index = HashCode(Key) & (Length - 1)

下面我们以值为“book”的Key来演示整个过程:

1.计算book的hashcode,结果为十进制的3029737,二进制的101110001110101110 1001。

2.假定HashMap长度是默认的16,计算Length-1的结果为十进制的15,二进制的1111。

3.把以上两个结果做与运算,101110001110101110 1001 & 1111 = 1001,十进制是9,所以 index=9。

可以说,Hash算法最终得到的index结果,完全取决于Key的Hashcode值的最后几位。

假设HashMap的长度是10,重复刚才的运算步骤:

HashCode:10 1110 0011 1010 1110 1001
Length-11001
Index:   1001

单独看这个结果,表面上并没有问题。我们再来尝试一个新的HashCode 101110001110101110 1011 :

HashCode:10 1110 0011 1010 1110 1011
Length-11001
Index:   1001

让我们再换一个HashCode 101110001110101110 1111 试试 :

HashCode:10 1110 0011 1010 1110 1111
Length-11001
Index:   1001

是的,虽然HashCode的倒数第二第三位从0变成了1,但是运算的结果都是1001。也就是说,当HashMap长度为10的时候,有些index结果的出现几率会更大,而有些index结果永远不会出现(比如0111)!

这样,显然不符合Hash算法均匀分布的原则。

反观长度16或者其他2的幂,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。

高并发下的HashMap

在高并发下使用HashMap,它的容量是有限的,所以HashMap会通过ReHash的方式进行扩容,使用ReHash能够使容量扩大本身的两倍。所以ReHash是在HashMap扩容时的一个步骤。当经过多次元素插入,使得HashMap达到一定的饱和度,key映射位置发生冲突的几率会逐渐增高。这个情况我们需要进行Resize,那么影响Resize的因素有两个:Capacity 初始长度 、LoadFactor 负载因子.

HashMap.Size >= Capacity * LoadFactor

HashMap的Resize经过两个步骤:

扩容:创建一个新的Entry空数组,长度是原来的两倍
ReHash:遍历原来的Entry数组,把所有的Entry重新Hash到新数组。因为长度扩大之后Hash的规则会发生变动,因此需要重新Hash。
回顾一下Hash公式:
**index = HashCode(Key) & (Length - 1)**

当原数组长度为8时,Hash运算是和111B做与运算;新数组长度为16,Hash运算是和1111B做与运算。Hash结果显然不同。

原文链接:https://blog.csdn.net/sinat_31011315/article/details/78699655

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值