写点什么

HashMap 源码分析 - 新增

作者:zarmnosaj
  • 2022 年 5 月 23 日
  • 本文字数:1801 字

    阅读完需:约 6 分钟

HashMap 源码分析-新增

新增

新增 key、value 的大概步骤:


  1. 判断空数组有无初始化,如果没有的话,则进行初始化

  2. 如果通过 key 的 hash 能够直接找到值,跳转到 6,否则到 3;

  3. 如果 hash 冲突,则判断是 链表 or 红黑树

  4. 如果是链表的话,进行递归循环,把新元素追加到队尾

  5. 如果是红黑树,调用红黑树新增的方法进行新增

  6. 将新元素追加成功,再判断是否需要覆盖

  7. 新增完后,判断是否需要扩容,需要则进行扩容


源码:


    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,                   boolean evict) {        Node<K,V>[] tab; Node<K,V> p; int n, i;        if ((tab = table) == null || (n = tab.length) == 0)            n = (tab = resize()).length;        if ((p = tab[i = (n - 1) & hash]) == null)            tab[i] = newNode(hash, key, value, null);        else {            Node<K,V> e; K k;            if (p.hash == hash &&                ((k = p.key) == key || (key != null && key.equals(k))))                e = p;            else if (p instanceof TreeNode)                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);            else {                for (int binCount = 0; ; ++binCount) {                    if ((e = p.next) == null) {                        p.next = newNode(hash, key, value, null);                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st                            treeifyBin(tab, hash);                        break;                    }                    if (e.hash == hash &&                        ((k = e.key) == key || (key != null && key.equals(k))))                        break;                    p = e;                }            }            if (e != null) { // existing mapping for key                V oldValue = e.value;                if (!onlyIfAbsent || oldValue == null)                    e.value = value;                afterNodeAccess(e);                return oldValue;            }        }        ++modCount;        if (++size > threshold)            resize();        afterNodeInsertion(evict);        return null;    }
复制代码


final V putVal(int hash, K key, V value, boolean onlyIfAbsent


  1. 入参 hash 表示 :通过 hash 算法计算出来的值。

  2. 入参 onlyIfAbsent 表示 :false 表示即使 key 已经存在了,仍然会用新值覆盖原来的值,默认为 false


Node<K,V>[] tab; Node<K,V> p; int n, in 表示数组的长度,i 表示数组索引的下标,p 是 i 下标位置的 Node 值


if ((tab = table) == null || (n = tab.length) == 0)数组为空时,则使用 resize 方法初始化


if ((p = tab[i = (n - 1) & hash]) == null)如果当前索引位置是空的,则直接生成新的节点在当前索引位置上


tab[i] = newNode(hash, key, value, null);如果当前索引位置有值的处理方法


Node<K,V> e; K k;e 当前节点的临时变量


if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))如果 key 的 hash 和值都相等,直接把当前下标位置的 Node 值赋值给临时变量


else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);如果是红黑树,使用红黑树的方式新增


for (int binCount = 0; ; ++binCount) {自旋


if ((e = p.next) == null) {e = p.next 表示从头开始,遍历链表 p.next == null 表明 p 是链表的尾节点


p.next = newNode(hash, key, value, null);把新节点放到链表的尾部


if (binCount >= TREEIFY_THRESHOLD - 1)当链表的长度大于等于 8 时,链表转红黑树


if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))链表遍历过程中,发现有元素和新增的元素相等,结束循环


p = e;更改循环的当前元素,使 p 在遍历过程中,一直往后移动。


if (e != null) {说明新节点的新增位置已经找到了


if (!onlyIfAbsent || oldValue == null)当 onlyIfAbsent 为 false 时,才会覆盖值


++modCount; 记录 HashMap 的数据结构发生了变化


if (++size > threshold)如果 HashMap 的实际大小大于扩容的门槛,开始扩容

用户头像

zarmnosaj

关注

还未添加个人签名 2020.02.06 加入

还未添加个人简介

评论

发布
暂无评论
HashMap 源码分析-新增_5 月月更_zarmnosaj_InfoQ写作社区