JDK 1.8 对 HashMap 除了红黑树还进行了哪些改动?

八股文一网打尽,更多面试题请看程序员面试刷题神器 - 面试鸭open in new window

回答重点

  • 改进了哈希函数的计算:JDK 1.8 中优化了哈希函数,使得哈希值的分布更加均匀,减少了哈希冲突的发生。通过在生成哈希值时使用“扰动函数”,确保哈希值的高低位都能参与到桶的选择中。
  • 扩容机制优化:JDK 1.8 改进了扩容时的元素迁移机制。在扩容过程中不再对每个元素重新计算哈希值,而是根据原数组长度的高位来判断元素是留在原位置,还是迁移到新数组中的新位置。这一改动减少了不必要的计算,提升了扩容效率。
  • 头插法变为尾插法:头插法的好处就是插入的时候不需要遍历链表,直接替换成头结点,但是缺点是扩容的时候会逆序,而逆序在多线程操作下可能会出现环,产生死循环,于是改为尾插法。

扩展知识

哈希函数的优化

1.7是这样实现的:

static int hash(int h) {
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

而 1.8 是这样实现的:

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

具体而言就是 1.7 的操作太多了,经历了四次异或,所以 1.8 优化了下,它将 key 的哈希码的高 16 位和低 16 位进行了异或,得到的 hash 值同时拥有了高位和低位的特性,使得哈希码的分布更均匀,不容易冲突。

这也是 JDK 开发者根据速度、实用性、哈希质量所做的权衡来做的实现:

20220219203008.png

扩容机制优化

头插法和尾插法

1.7 是头插法,头插法的好处就是插入的时候不需要遍历链表,直接替换成头结点,但是缺点是扩容的时候会逆序,而逆序在多线程操作下可能会出现环,然后就死循环了。

20220219203032.png

然后 1.8 是尾插法,每次都从尾部插入的话,扩容后链表的顺序还是和之前一致,所以不可能出现多线程扩容成环的情况。

再延伸一下,改成尾插法之后 HashMap 就不会死循环了吗?

好像还是会,这次是红黑树的问题,我在网上看到这篇文章,有兴趣的可以深入了解下:

https://blog.csdn.net/qq_33330687/article/details/101479385

八股文一网打尽,更多面试题请看程序员面试刷题神器 - 面试鸭open in new window

最近更新:
Contributors: weave