ConcurrentHashMap之get(k)方法源码分解

CONCURRENT中的get方法

    public V get(Object key) {
        //tab 引用map.table
        //e 当前元素
        //p 目标节点
        //n table数组长度
        //eh 当前元素hash
        //ek 当前元素key
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        //扰动运算后得到 更散列的hash值
        int h = spread(key.hashCode());

        //条件一:(tab = table) != null
        //true->表示已经put过数据,并且map内部的table也已经初始化完毕
        //false->表示创建完map后,并没有put过数据,map内部的table是延迟初始化的,只有第一次写数据时会触发创建逻辑。
        //条件二:(n = tab.length) > 0 true->表示table已经初始化
        //条件三:(e = tabAt(tab, (n - 1) & h)) != null
        //true->当前key寻址的桶位 有值
        //false->当前key寻址的桶位中是null,是null直接返回null
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            //前置条件:当前桶位有数据

            //对比头结点hash与查询key的hash是否一致
            //条件成立:说明头结点与查询Key的hash值 完全一致
            if ((eh = e.hash) == h) {
                //完全比对 查询key 和 头结点的key
                //条件成立:说明头结点就是查询数据
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }

            //条件成立:
            //1.-1  fwd 说明当前table正在扩容,且当前查询的这个桶位的数据 已经被迁移走了
            //2.-2  TreeBin节点,需要使用TreeBin 提供的find 方法查询。
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;




            //当前桶位已经形成链表的这种情况
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }

        }
        return null;
    }

然后就来到了ForwardingNode的find(int h, Object k)

        Node<K,V> find(int h, Object k) {
            // loop to avoid arbitrarily deep recursion on forwarding nodes
            //tab 一定不为空
            Node<K,V>[] tab = nextTable;
            outer: for (;;) {
                //n 表示为扩容而创建的 新表的长度
                //e 表示在扩容而创建新表使用 寻址算法 得到的 桶位头结点
                Node<K,V> e; int n;

                //条件一:永远不成立
                //条件二:永远不成立
                //条件三:永远不成立
                //条件四:在新扩容表中 重新定位 hash 对应的头结点
                //true -> 1.在oldTable中 对应的桶位在迁移之前就是null
                //        2.扩容完成后,有其它写线程,将此桶位设置为了null
                if (k == null || tab == null || (n = tab.length) == 0 ||
                    (e = tabAt(tab, (n - 1) & h)) == null)
                    return null;

                //前置条件:扩容后的表 对应hash的桶位一定不是null,e为此桶位的头结点
                //e可能为哪些node类型?
                //1.node 类型
                //2.TreeBin 类型
                //3.FWD 类型

                for (;;) {
                    //eh 新扩容后表指定桶位的当前节点的hash
                    //ek 新扩容后表指定桶位的当前节点的key
                    int eh; K ek;
                    //条件成立:说明新扩容 后的表,当前命中桶位中的数据,即为 查询想要数据。
                    if ((eh = e.hash) == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;

                    //eh<0
                    //1.TreeBin 类型    2.FWD类型(新扩容的表,在并发很大的情况下,可能在此方法 再次拿到FWD类型..)
                    if (eh < 0) {
                        if (e instanceof ForwardingNode) {
                            tab = ((ForwardingNode<K,V>)e).nextTable;
                            continue outer;
                        }
                        else
                            //说明此桶位 为 TreeBin 节点,使用TreeBin.find 查找红黑树中相应节点。
                            return e.find(h, k);
                    }

                    //前置条件:当前桶位头结点 并没有命中查询,说明此桶位是 链表
                    //1.将当前元素 指向链表的下一个元素
                    //2.判断当前元素的下一个位置 是否为空
                    //   true->说明迭代到链表末尾,未找到对应的数据,返回Null
                    if ((e = e.next) == null)
                        return null;
                }
            }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值