ConcurrentHashMap之remove()方法源码分解

public V remove(Object key) { return replaceNode(key, null, null); }

    /**
     * Implementation for the four public remove/replace methods:
     * Replaces node value with v, conditional upon match of cv if
     * non-null.  If resulting value is null, delete.
     */
    final V replaceNode(Object key, V value, Object cv) {
        //计算key经过扰动运算后的hash
        int hash = spread(key.hashCode());
        //自旋
        for (Node<K,V>[] tab = table;;) {
            //f表示桶位头结点
            //n表示当前table数组长度
            //i表示hash命中桶位下标
            //fh表示桶位头结点 hash
            Node<K,V> f; int n, i, fh;

            //CASE1:
            //条件一:tab == null  true->表示当前map.table尚未初始化..  false->已经初始化
            //条件二:(n = tab.length) == 0  true->表示当前map.table尚未初始化..  false->已经初始化
            //条件三:(f = tabAt(tab, i = (n - 1) & hash)) == null true -> 表示命中桶位中为null,直接break, 会返回
            if (tab == null || (n = tab.length) == 0 ||
                (f = tabAt(tab, i = (n - 1) & hash)) == null)
                break;

            //CASE2:
            //前置条件CASE2 ~ CASE3:当前桶位不是null
            //条件成立:说明当前table正在扩容中,当前是个写操作,所以当前线程需要协助table完成扩容。
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);

            //CASE3:
            //前置条件CASE2 ~ CASE3:当前桶位不是null
            //当前桶位 可能是 "链表" 也可能 是  "红黑树" TreeBin
            else {
                //保留替换之前的数据引用
                V oldVal = null;
                //校验标记
                boolean validated = false;
                //加锁当前桶位 头结点,加锁成功之后会进入 代码块。
                synchronized (f) {
                    //判断sync加锁是否为当前桶位 头节点,防止其它线程,在当前线程加锁成功之前,修改过 桶位 的头结点。
                    //条件成立:当前桶位头结点 仍然为f,其它线程没修改过。
                    if (tabAt(tab, i) == f) {
                        //条件成立:说明桶位 为 链表 或者 单个 node
                        if (fh >= 0) {
                            validated = true;

                            //e 表示当前循环处理元素
                            //pred 表示当前循环节点的上一个节点
                            Node<K,V> e = f, pred = null;
                            for (;;) {
                                //当前节点key
                                K ek;
                                //条件一:e.hash == hash true->说明当前节点的hash与查找节点hash一致
                                //条件二:((ek = e.key) == key || (ek != null && key.equals(ek)))
                                //if 条件成立,说明key 与查询的key完全一致。
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    //当前节点的value
                                    V ev = e.val;

                                    //条件一:cv == null true->替换的值为null 那么就是一个删除操作
                                    //条件二:cv == ev || (ev != null && cv.equals(ev))  那么是一个替换操作
                                    if (cv == null || cv == ev ||
                                        (ev != null && cv.equals(ev))) {
                                        //删除 或者 替换

                                        //将当前节点的值 赋值给 oldVal 后续返回会用到
                                        oldVal = ev;

                                        //条件成立:说明当前是一个替换操作
                                        if (value != null)
                                            //直接替换
                                            e.val = value;
                                        //条件成立:说明当前节点非头结点
                                        else if (pred != null)
                                            //当前节点的上一个节点,指向当前节点的下一个节点。
                                            pred.next = e.next;

                                        else
                                            //说明当前节点即为 头结点,只需要将 桶位设置为头结点的下一个节点。
                                            setTabAt(tab, i, e.next);
                                    }
                                    break;
                                }
                                pred = e;
                                if ((e = e.next) == null)
                                    break;
                            }
                        }

                        //条件成立:TreeBin节点。
                        else if (f instanceof TreeBin) {
                            validated = true;

                            //转换为实际类型 TreeBin t
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            //r 表示 红黑树 根节点
                            //p 表示 红黑树中查找到对应key 一致的node
                            TreeNode<K,V> r, p;

                            //条件一:(r = t.root) != null 理论上是成立
                            //条件二:TreeNode.findTreeNode 以当前节点为入口,向下查找key(包括本身节点)
                            //      true->说明查找到相应key 对应的node节点。会赋值给p
                            if ((r = t.root) != null &&
                                (p = r.findTreeNode(hash, key, null)) != null) {
                                //保存p.val 到pv
                                V pv = p.val;

                                //条件一:cv == null  成立:不比对value,就做替换或者删除操作
                                //条件二:cv == pv ||(pv != null && cv.equals(pv)) 成立:说明“对比值”与当前p节点的值 一致
                                if (cv == null || cv == pv ||
                                    (pv != null && cv.equals(pv))) {
                                    //替换或者删除操作


                                    oldVal = pv;

                                    //条件成立:替换操作
                                    if (value != null)
                                        p.val = value;


                                    //删除操作
                                    else if (t.removeTreeNode(p))
                                        //这里没做判断,直接搞了...很疑惑
                                        setTabAt(tab, i, untreeify(t.first));
                                }
                            }
                        }
                    }
                }
                //当其他线程修改过桶位 头结点时,当前线程 sync 头结点 锁错对象时,validated 为false,会进入下次for 自旋
                if (validated) {

                    if (oldVal != null) {
                        //替换的值 为null,说明当前是一次删除操作,oldVal !=null 成立,说明删除成功,更新当前元素个数计数器。
                        if (value == null)
                            addCount(-1L, -1);
                        return oldVal;
                    }
                    break;
                }
            }
        }
        return null;
    }    public V remove(Object key) {
        return replaceNode(key, null, null);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值