【Java】HashMap原理-JDK1.7与JDK1.8的区别

一、HashMap 扩容

JDK1.7 和JDK1.8 扩容原理相同

HashMap初始化大小为16, 负载因子为0.75,每次当容量大于16 * 0.75 时, 进行扩容,扩容为原来的两倍。也可以通过构造方法修改,但HashMap会自动将给定初始化大小扩容到2的幂次方。

  • JDK1.7: 扩容后需要重新计算hashcode值
  • JDK1.8:扩容后无需重新计算hashcode值。(具体看后面“HashMap的容量为2的幂次方”部分)

二、HashMap的冲突解决

JDK1.7以前

  • HashMap使用 数组 + 链表的方式来解决哈希冲突,并使用头插法
  • 使用头插法的原因:一般情况下,新加入的键值对被查找访问的概率更高。 当链表长度大于阈值(默认为8)时,就会对数组进行扩容。

JDK1.8以后

  • HashMap使用 数组 + 链表 + 红黑树的方式来解决哈希冲突,并使用尾插法
  • 当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
  • 使用尾插法的原因:因为尾插法可以统计链表长度,判断是否大于8,而且链表过长时会转化为红黑树,所以时间浪费不高。
  • 使用红黑树的原因:相比于平衡二叉排序树,红黑树只有在最长路径>最短路径*2时才触发旋转操作。红黑树的查找删除时间复杂度为O(logn)。

补充:将链表转为红黑树前会判断,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树。

三、为什么HashMap的容量为2的幂次方

(1)为了找到key的位置在哈希表的哪个槽里,需要计算hash(KEY) % length。若使length为2的幂次方,可以将取余操作变为hash(KEY) & (length - 1),将取余操作变换为位运算从而提高运算的效率。

举例说明

可以看下这个例子,这个例子来自灰信网
假如array.length = 2^4 = 16,二进制10000。这个数减去1的结果是1111,也就是array.length -1 = 1111。
再假设一个key的hashCode值为10011011001,与1111做 & 操作,得到的结果是1001(高位部分1001101都舍去了)。而1001(二进制)必然是一个小于10000(数组长度二进制)的数,对于一个小于10000(数组长度二进制)的数而言,1001 % 10000得到的就是1001(二进制)自己。
那么刚刚舍弃的高位部分1001101 0000(后面补上了四个0000)就一定能被10000整除吗?答案是肯定的:因为10011010000可以拆成10000000000+10000000+1000000+10000,这几个数都能通过10000的n次左移得到,也就相当于这几个数都能被10000整除。那他们的和,也就是10011010000,一定也可以被10000整除。
因此,最终结论就是:10011011001 & ( 10000 - 1 ) = 10011011001 & 1111 = 1001 = 10011011001 % 10000

(2)进行扩容时,需要移动的数据较少。每次扩容相当于数组长度的高位多一个1,新的hash运算取决于hashcode在这一位上的值是0还是1,是0则无需变换位置,是1则位置为原来位置+原来数组长度 的位置。

举例说明

例如,原来长度为16,扩容后为32。则16-1的二进制为0000 1111,32-1的二进制为0001 1111。
假设一个KEY通过哈希函数计算得到的哈希值为52(二进制为0011 0100),在扩容前 将0011 0100与0000 1111进行与运算,结果为0000 0100。扩容后,将0011 0100与0001 1111进行与运算,结果为 0001 0100。
扩容后相当于高位多了个1,根据与运算的特性,只有在原哈希值在这一位上为1时,最后与运算的结果也为1。
因为扩容后最高位肯定会变为1,所以只要判断原哈希值这一位上是0还是1就可以决定扩容后是否要移位。如果是0,扩容前后与运算的结果一样,则表示新位置与旧位置相同,不需要移位。如果为1,则要移位。
那移动到的新位置其实就是高位多个1,也就等价于在原来的位置上加上原数组长度的位置。
这个例子的详细图解可以看jdk8之HashMap resize方法详解(深入讲解为什么1.8中扩容后的元素新位置为原位置+原数组长度

本文参考如下链接:

通俗易懂HASHMAP为何喜欢2的倍数扩容,(数组容量是2的次幂)

jdk8之HashMap resize方法详解(深入讲解为什么1.8中扩容后的元素新位置为原位置+原数组长度)
HashMap底层为什么是2倍扩容?

你可能感兴趣的:(Java,java,散列表,数据结构,HashMap)