Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >面试官:JDK1.8 HashMap扩容rehash算法是如何优化的?

面试官:JDK1.8 HashMap扩容rehash算法是如何优化的?

作者头像
三友的java日记
发布于 2022-07-27 05:44:45
发布于 2022-07-27 05:44:45
68700
代码可运行
举报
文章被收录于专栏:原创干货原创干货
运行总次数:0
代码可运行

本文跟大家聊一聊一个常见的面试题,那就是JDK1.8 HashMap扩容rehash算法是如何优化的?

众所周知HashMap的底层其实是一个数组,既然是一个数组,必然长度是固定的,也就一定存在扩容的问题。在JDK1.7的时候,是将数组扩容为两倍,然后将HashMap中所有的key重新进行hash寻址算法然后再放入到扩容后的新的数组的新的位置。

但是从JDK1.8之后,对rehash进行了优化,减少了对key重新进行hash寻址算法的过程,具体怎么实现的,这就上源码。

我们都知道HashMap扩容是通过resize来实现的,所以我们看看resize方法的实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //这个 newCap = oldCap << 1 就是扩容两倍的证据
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        //重新构建了一个新的数组,容量是上面计算出来的newCap
        //就是原来的两倍大小
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //拿到老的数组,然后遍历每个数组的位置,对每个节点重新进行rehash
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                //当遍历到这个数组的位置有节点的时候,进入重新rehash
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                    //这个的意思很简单,就是这个节点只有一个
                    //也就是没有形成链表或者红黑树的时候
                    //此时处理就是重新进行hash寻址算法,找到在新数组的位置放上
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                    //就是这个已经是红黑树了,此时会进入红黑树rehash的过程
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                    //这个就是链表的rehash的过程
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

其实rehash就是遍历数组的每个位置,判断节点的状态,是单个或者链表或者红黑树。接下来就每种情况进行讨论。

1)单个节点

其实重新进行hash寻址算法,找到对应数组的下标,放上就行了

2)链表

仔细阅读源码会发现,就是将之前的链表rehash之后重新拆分为了两个链表,一个链表rehash之后还是在当前的位置index,另一个链表rehash之后的位置变成了index + oldCap,画个图理解一下

至于为什么可以分为两个链表,这里说明一下。就是hash寻址算法对一个数组下标的所有节点,扩容后进行重新计算的时候,会发现计算出来的位置要么是在原来的index,要么实在原来的index + oldCap的位置,这是hash寻址的一个特点,所以基于这一个既定的结论,就去判断一下每个节点重新hash寻址之后是原来的位置还是index + oldCap的位置就行了(如何判断,就是源码图的第一个红框框出来的),判断是在原来的位置然后一个新的链表,在index + oldCap的位置也形成一个新的链表,这样计算完之后只要把新的两个链表挂在新的数组的 index 和 index + oldCap就行了(如何挂的,就是源码图的第二个红框框出来的)。这样就避免了对每个节点重新进行hash寻址算法,重新放到hash表中的过程,大大提高了效率,这也就是JDK1.8的HashMap扩容rehash算法优化。

3)红黑树

贴上源码

其实原理跟链表的差不多,就是链表拆成两个链表,红黑树这个拆成两个红黑树,分别挂到新的数组的位置上,只不过最后加个判断,就是判断这个红黑树是需要变成链表还是继续是红黑树。

所以在JDK1.8的rehash算法优化就是对原来的链表或者红黑树进行拆分成两部分,然后分别挂在原来数组的位置和 数组的位置 + oldCap的位置,这样做就避免了大量的节点进行重新hash寻址算法和重新放到hash表的过程,大大增加了扩容效率。

本文完。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 三友的java日记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
jdk1.8 HashMap扩容机制变化「建议收藏」
JDK1.8中的HashMap较于前代有了较大的变更,主要变化在于扩容机制的改变。在JDK1.7及之前HashMap在扩容进行数组拷贝的时候采用的是头插法,因此会造成并发情景下形成环状链表造成死循环的问题。JDK1.8中改用了尾插法进行数组拷贝,修复了这个问题。
全栈程序员站长
2022/06/28
4390
jdk1.8 HashMap扩容机制变化「建议收藏」
HashMap扩容机制
HashMap的扩容机制 上一期已经讲到了添加元素的put方法了,现在回顾一下put方法,主要讲解扩容方法:
用户6055494
2019/10/15
9520
八、JDK1.8中HashMap扩容机制
前面文章一、深入理解-Java集合初篇 中我们对Java的集合体系进行一个简单的分析介绍,上两篇文章二、Jdk1.7和1.8中HashMap数据结构及源码分析 、三、JDK1.7和1.8HashMap数据结构及源码分析-续 中我们分别对JDK1.7和JDK1.8中HashMap的数据结构、主要声明变量、构造函数、HashMap的put操作方法做了深入的讲解和源码分析。 四、深入理解Java中的HashMap「网易面试快答」文章中主要针对面试中常见的面试问题进行简单解答。 五、深入理解JDK1.7中HashMap哈希冲突解决方案 和 六、深入理解JDK1.8中HashMap哈希冲突解决方案 中对HashMap中哈希冲突及减少哈希冲突的解决方案做详细的介绍,并通过源码加深大家的理解。 七、JDK1.7中HashMap扩容机制 中介绍了JDK1.7中HashMap的扩容机制及扩容过程中可能出现的死锁及数据丢失问题。 本篇文章我们将要介绍JDK1.8中HashMap的扩容机制,并通过一个实例来展示链表的哈希扩容。
全栈程序员站长
2022/07/04
4910
八、JDK1.8中HashMap扩容机制
JDK1.8 HashMap源码学习
get()方法: 主要是调用getNode()方法,如果找不到返回null 先调用hash计算哈希值,获取通下标,然后再判断第一个节点是否为红黑树,如果是红黑树,则通过树的方式获取,否则还是按照链表的方式获取
Li_XiaoJin
2022/06/10
2280
JDK1.8 HashMap源码学习
Java集合-HashMap源码解析-JDK1.8
HashMap在jdk 1.8中使用用的是数组+链表+红黑树的结构来进行存储的,请看下图:
Java学习录
2019/04/18
3200
Java集合-HashMap源码解析-JDK1.8
HashMap的put kv,是如何扩容的?
真正发生影响的是新增的那一位(红色箭头所指),所以 oldCap & hash 完全可以判断该值是放在旧索引值的位置还是放在旧索引值+旧数组长度的位置。
程序员小航
2020/11/23
4920
HashMap的put kv,是如何扩容的?
深入解析HashMap原理(基于JDK1.8)
之前经常用HsahMap但是从未了解过底层的实现原理,今天就基于jdk1.8来研究一下HashMap的底层实现。
xcbeyond
2020/03/25
3550
深入解析HashMap原理(基于JDK1.8)
HashMap的底层实现-JDK1.8之后
在JDK 1.8及之后的版本中,HashMap的底层实现进行了优化,主要改进了处理哈希冲突的方式。具体来说,当链表长度超过一定阈值(默认为8)时,链表会转换为红黑树,以提高查找效率。这种优化使得HashMap在处理大量哈希冲突时性能更好。
代码小李
2025/01/30
1250
HashMap 如何解决冲突?扩容机制?
HashMap默认的容量是DEFAULT_INITIAL_CAPACITY,16。
暮雨
2019/10/20
9050
源码解析HashMap底层扩容
HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,采用哈希表来存储的
小明爱吃火锅
2023/11/25
1820
因为没给学弟讲明白HashMap的扩容机制,小二哭的稀里哗啦~
HashMap 发出的 Warning:这是《Java 程序员进阶之路》专栏的第 56 篇。那天,小二垂头丧气地跑来给我诉苦,“老王,有个学弟小默问我‘ HashMap 的扩容机制’,我愣是支支吾吾讲了半天,没给他讲明白,讲到最后我内心都是崩溃的,差点哭出声!”
沉默王二
2021/09/26
4640
简述HashMap的扩容机制
注意:本博客需要对HashMap源码有过一定理解,看过源码比较好,仅供互相学习参考
SmileNicky
2023/07/24
3310
简述HashMap的扩容机制
JDK8的HashMap源码学习笔记
正文: 概念 HashMap是数组+链表+红黑树实现的,红黑树是在JDK8中增加的,优化了链表过长的效率问题 HashMap 泊松分布 HashMap源码注释有提到这个概念,泊松分布是单位时间内独立事
itliusir
2018/05/21
1.2K0
HashMap扩容机制解析
通过查看Java JDK1.8putVal()源码可看到,有两种情况可能会触发扩容。
全栈开发日记
2022/05/13
2640
HashMap扩容机制解析
深入解析Java HashMap的Resize源码
Java中的HashMap是一个常用的数据结构,底层实现由数组和链表(或红黑树)组成。随着插入元素的增多,HashMap需要扩容以维持高效的性能。本文将深入解析HashMap的扩容机制——resize()方法,通过逐行代码解释其实现原理和背后的设计思想。
九转成圣
2024/06/06
1870
不怕面试再问HashMap,一次彻底地梳理(原理+手写实现)
朋友们又见面了,你是不是还在面试时被面试官问懵HashMap?不会手写实现一个简单HashMap?看完这篇文章你再不会算我输!
Java程序猿阿谷
2021/03/02
4180
不怕面试再问HashMap,一次彻底地梳理(原理+手写实现)
JDK1.8源码分析之HashMap
HashMap是一种Map,HashMap仅是一种Map的实现版本,下面的图片展示了java中Map的一些实现版本:
九州暮云
2019/08/21
3710
死磕Java之聊聊HashMap源码(基于JDK1.8)
HashMap是Java程序员使用频率最高的数据结构之一。另外,JDK1.8对HashMap底层的实现进行了优化,如引入红黑树的数据结构以及扩容的优化等等来提高性能。本文结合JDK1.8的源码,探讨HashMap的结构实现和功能原理。
haifeiWu
2018/09/11
6580
死磕Java之聊聊HashMap源码(基于JDK1.8)
HashMap 中的容量与扩容实现,细致入微,值的一品!
    巴闭,你的脚怎么会有味道,我要闻闻看是不是好吃的,嗯~~爸比你的脚臭死啦!! ……
青石路
2019/11/12
6360
经常被面试官问到的HashMap,详细解读看这一篇就够了
https://juejin.im/post/5d09f2d56fb9a07ec7551fb0
南风
2019/07/08
1.2K0
经常被面试官问到的HashMap,详细解读看这一篇就够了
相关推荐
jdk1.8 HashMap扩容机制变化「建议收藏」
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档