现在的位置: 首页 > 架构设计 > 正文

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

2020年02月19日 架构设计 ⁄ 共 1229字 ⁄ 字号 评论关闭

  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[] tab, int hash)` 。

  4. 扩容

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

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

抱歉!评论已关闭.