HashMap 中的 put 方法有哪些骚操作?

Photo By Instagram taeri_taeri

昨天的问题

HashMap 想必是平时工作中使用非常的频繁的数据结构了吧,它简单易用,同时也是面试扫盲第一轮技术面中出现频率极高的题目之一。假设我问你:HashMap 一次 put 操作的详细过程是怎样的,你可以对答如流吗?

我的答案

HashMap 的存储结构就不用多介绍了,底层是一个 列表/二叉树 的数组结构。当我们调用一次 put 操作的时候主要会经过如下 4 个大步骤,让我们来详细看一下这四个步骤都做了些什么事情。

1. 求 Key 的 hash 值

想要存入 HashMap 的元素的 key 都必须要经过一个 hash 运算,首先调用 key 对象的 hashCode 方法获取到对象的 hash 值。然后将获得 hash 值向右位移 16 位,接着将位移后的值与原来的值做异或运算,即可获得到一个比较理想的 hash 值,之所以这样运算时为了尽可能降低 hash 碰撞(即使得元素均匀分布到各个槽中)。

2. 确定 hash 槽

获取到 hash 值后就是计算该元素所对应的 hash 槽,这一步操作比较简单。直接将上一步操作中获取到的 hash 值与底层数组长度取模(为了提高性能,源码采用了运算,这样等价于取模运算,但是性能更好)获取 index 位置。

3. 将元素放入槽中

在上一步中我们获得了 hash 槽的索引,接下来就是将元素放入到槽中,如果槽里当前没有任何元素则直接生成 Node 放入槽中,如果槽中已经有元素存在,则会有下面 2 种情况:

3.1 槽里是一颗平衡二叉树

当列表的元素长度超过 8 时,为了加快槽上元素的索引速度,HashMap 会将列表转换为平衡二叉树。所以在这种情况下,插入元素是在一颗平衡二叉树上做操作,查找和更新的时间复杂度都是 log(n),HashMap 会将元素包装为一个 TreeNode 添加到树上。具体平衡二叉树的操作就不在此展开了,有兴趣的小伙伴可自行探索。

3.2 槽里是一个列表

初始情况下,槽里面的元素是以列表形式存在的,HashMap 遍历列表将元素 更新 / 追加 到列表尾部。元素添加后,HashMap 会判断当前列表元素个数,如达到 8 个元素则将列表转化为平衡二叉树,具体转换详情可参考 HashMap 中的方法 `final void treeifyBin(Node<K,V>[] tab, int hash)` 。

4. 扩容

到这里时候,我们元素已经完美的放到了 HashMap 的存储中。但是还有一个 HashMap 的自动扩容操作需要完成,默认情况下自动扩容因子是 0.75,即当容量为 16 时候,如果存储元素达到 16 * 0.75 = 12 个的时候,HashMap 会进行 resize 操作(俗称 rehash)。HashMap 会新建一个 2 倍容量与旧数组的数组,然后依次遍历旧的数组中的每个元素,将它们转移到新的数组当中。其中有 2 个需要注意的点:列表中元素依然会保持原来的顺序和加入二叉树上的元素少达 6 个时候会将二叉树再次转化为列表来存储。

如上我们介绍了 HashMap 的依次 put 操作的全过程,小伙伴们快去看源码巩固一下吧,欢迎留言与我交流哦。

以上即为昨天的问题的答案,小伙伴们对这个答案是否满意呢?欢迎留言和我讨论。

又要到年末了,你是不是又悄咪咪的开始看机会啦。为了广大小伙伴能充足电量,能顺利通过 BAT 的面试官无情三连炮,我特意推出大型刷题节目。每天一道题目,第二天给答案,前一天给小伙伴们独立思考的机会。

点下“在看”,鼓励一下?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值