HashMap中的hashcode作用
HashMap是Java 中很重要的一个概念,工作中使用的频率也非常广泛,需要对其进行了解。
看源码是很枯燥的,但是看懂了,却有种豁然开朗的感觉,觉得特别棒,本篇只说hashcode的作用及数组长度为什么是2的n次幂。
首先点开HashMap的类,我是用开发工具idea ,新建了一个HashMap,点进去找到put方法。以jdk 1.8为例,如下:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
然后再点进putVal 方法,则会看到有下面的代码:
tab[i = (n - 1) & hash]
1、HashMap 中为什么需要一个hashCode 值?
原因就是需要用它来对HashMap 数组的位置来定位,如果向HashMap 里存一个数,单纯的依次使用equals 方法比较key 是否相同来确定当前数据是否已存储过,那效率非常低,而通过比较hashCode 值,效率就会大大提高,那它是如何定位数组位置呢,如果你使用的是jdk 1.8,那在put方法中的putVal方法里会看到如下内容:
对于上边的n-1 后边会说到,先说一下上边的写法,& 为二进制中的与运算 ,它的运算特点是,两个数进行& ,如果都为1,则运算结果为1,否则为0。
因为hashMap 的数组长度都是2的n次幂 ,那么对于这个数再减去1,转换成二进制的话,就肯定是最高位为0,其他位全是1 的数,那以数组长度为8为例(默认HashMap初始数组长度是16),那8-1 转成二进制的话,就是0111 。
那我们举一个随便的hashCode值,与0111 进行与运算 看看结果如何,如下:
第一个key: hashcode值:10101000
与0111进行&运算 & 0111
0000 (十进制为0)
------------------------------------------
第二个key: hashcode值:11101000
与0111进行&运算 & 0111
0000 (十进制为0)
--------------------------------------------
第三个key: hashcode值:11101010
与0111进行&运算 & 0111
0010 (十进制为2)
你可以随便变hashcode 值来测试,最终得到的数都会小于8 ,当然会像上边一样,出现相同的数据,那样的话,就会以链表的形式存在那个数组元素上了。
回过头来再设想一下,那我们就可以通过把key转为hashCode值,然后与数组长度进行与运算 ,来确定HahsMap 中当前key对应的数据在数组中的位置 。
原本,需要查找数组下的每个元素,以及他们对应的链,疯狂的调用equals 方法,誓死遍历出数据的方式,变成了仅仅是查找数组下的一个元素,然后,只需要equals 比较这一条链上的数据就可以了,这样equals 的使用次数降低了很多。
通过上边的说明,不知道大家是否理解到了hashCode 值,在HashMap 中的作用呢?
2、为什么hashMap 的数组长度为2的n次幂?
还是上边那行代码:
tab[i = (n - 1) & hash]
我们知道了& 的作用,但是n-1 到底是什么意思呢?
其实上边,已经提到过了,就是2的n次幂 是很特殊的数,随便满足这个条件的数,对它减去1,转换成二进制,都会有这样的特点,即,最高位为0,其他低位全为1 。
就以数组长度分别为8和7 为例,看下边的情况:
数字8减去1转换成二进制是0111,即下边的情况
第一个key: hashcode值:10101001
与0111进行&运算 & 0111
0001 (十进制为1)
------------------------------------------
第二个key: hashcode值:11101000
与0111进行&运算 & 0111
0000 (十进制为0)
--------------------------------------------
第三个key: hashcode值:11101110
与0111进行&运算 & 0111
0110 (十进制为6)
这样得到的数,就会完整的得到原hashcode 值的低位值,不会受到与运算对数据的变化影响。
数字7减去1转换成二进制是0110,即下边的情况
第一个key: hashcode值:10101001
与0111进行&运算 & 0110
0000 (十进制为0)
------------------------------------------
第二个key: hashcode值:11101000
与0111进行&运算 & 0110
0000 (十进制为0)
--------------------------------------------
第三个key: hashcode值:11101110
与0111进行&运算 & 0111
0110 (十进制为6)
通过上边可以看到,当数组长度不为2的n次幂 的时候,hashCode 值与数组长度减一做与运算 的时候,会出现重复的数据,因为不为2的n次幂 的话,对应的二进制数肯定有一位为0 ,这样,不管你的hashCode 值对应的该位,是0 还是1 ,最终得到的该位上的数肯定是0 ,这带来的问题就是HashMap 上的数组元素分布不均匀,而数组上的某些位置,永远也用不到。如下图所示:

这将带来的问题就是你的HashMap 数组的利用率太低,并且链表可能因为上边的(n - 1) & hash 运算结果碰撞率过高,导致链表太深。(当然jdk 1.8已经在链表数据超过8个以后转换成了红黑树的操作,但那样也很容易造成它们之间的转换时机的提前到来)。
以上即是我理解的HashMap这方面的知识。
本文深入探讨了HashMap中hashCode的作用及其对数组位置定位的影响,并解析了数组长度为何选择2的n次幂的原因。
951

被折叠的 条评论
为什么被折叠?



