HashMap,这些都搞懂,再也不怕面试了

214 阅读4分钟

相信很多人都知道,在JDK8中,HashMap的容量总是2的n次幂,那么这么设计的目的究竟是什么呢?我可不可以将默认的初始容量从16改成20呢,扩容的时候我可不可以将扩容两倍改成扩容3倍或者1.5倍呢?

那么,带着这些问题,让我们一探究竟。

前期知识准备

什么是 ^

在Java里, ^ 是位运算的一种, 叫做亦或运算。它是针对二进制的一种运算方式,具体的运算规则是:相同的为0,不同的为1。

下面我们来举个

计算2^3的结果。

首先我们将2 和 3 转换为二进制的表现方式,如下:

2 ——> 0010

3 ——> 0011

那么根据上面描述的运算规则,结果就是0001,转换成十进制结果就是1。

怎么样,是不是很好理解。

什么是 &

在Java里, & 也是位运算的一种, 叫做与运算。它是针对二进制的一种运算方式,具体的运算规则是:只要有一个为0,就为0。

下面我们还是来举个

计算2&3的结果。

首先我们将2 和 3 转换为二进制的表现方式,如下:

2 ——> 0010

3 ——> 0011

那么根据上面描述的运算规则,结果就是0010,转换成十进制结果就是2。

什么是 >>>

在Java里, >>> 叫做无符号右移运算。它是针对二进制的一种运算方式,具体的运算规则是:忽略符号位,向右移动n位,空位都以0补齐。

下面我们来举个

计算16>>>2的结果。

首先我们将16 转换为二进制的表现方式,如下:

16 ——> 0001 0000

那么根据上面描述的运算规则,我们需要将这个二进制数右移两位,空位以0补齐,那么下面我用一张图来演示;

如上图所示,红框内是我们需要的结果,第一行为16代表的二级制的原始值,第二行将其右移两位,发现最后两位0移出红框,那么高位就缺两位,我们按照规则,用0将其补齐,得到第三行的数据,那么第三行数据代表的值就是16>>>2的结果,我们将其转化为十进制,结果为4。

所以说16>>>2的结果为4。

那么在Java中,这种类似的位运算还有很多,比如^,&,<<,>>,<<<,>>>等,感兴趣的同学可以自行百度学习。

hash算法

那么接下来进入我们今天的主题,hash算法。首先我们来看一下,hashmap中,hash算法是如何实现的。

那么,我们将上述代码的关键部分拆解来看,首先我们看一下

h = key.hashCode()

这段代码没有什么特殊的地方,就是求一下key的hash值。

接下来我们看第二段代码,

h >>> 16

这段代码的含义就是将key的hash值右移16位,具体的右移操作我们在上述知识准备中有提到过。

那么整体来看,

(h = key.hashCode()) ^ (h >>> 16)

这段代码含义就是,将key的hash值高16位和低16位进行按位异或的运算,这样可以保证hash值的高低16位都可以参与运算,可以更好的保留hash值每一位数值的特征,从而可以更好的降低hash冲突。

那么为什么采取 ^ 运算,而不是采取 & 或者 | 运算了,这样不是也可以保留高低十六位的特征么?大家可以思考一下,有想法的同学也可以在下方留言。

现在我们知道了HashMap中hash算法的细节,那么文章开头的问题你是否有思路呢,我们将在下一篇文章中进行解答。

注:本文内容全部基于jdk8讲述。