HashMap 源码分析-新增

125 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

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, i n 表示数组的长度,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 的实际大小大于扩容的门槛,开始扩容