V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
weidalao
V2EX  ›  Java

ConcurrentHashMap put 时 ,为什么 if(fh >= 0) else if (f instanceof TreeBin) 求大佬解惑

  •  
  •   weidalao · 2023-09-13 22:25:28 +08:00 · 1006 次点击
    这是一个创建于 418 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ConcurrentHashMap put 有步操作真没看懂

    我只能放一部分源码 问题是一个 if 和 else if 我怎么也想不明白 Node 的 hash 值 肯定是正数,通过 spread(key.hashCode()) 方法得到的 无论是红黑树还是链表,hash 值都是正数,所以这个 if 恒成立呀为啥还有个 else if 判断是否是红黑树,是红黑树的话用链表的方式再第一个 if 里插入不也错误的吗

    final V putVal(K key, V value, boolean onlyIfAbsent) {
            if (key == null || value == null) throw new NullPointerException();
            int hash = spread(key.hashCode());
            int binCount = 0;
            for (Node<K,V>[] tab = table;;) {
                Node<K,V> f; int n, i, fh;
                if (tab == null || (n = tab.length) == 0)
                    tab = initTable();
                else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                    if (casTabAt(tab, i, null,
                                 new Node<K,V>(hash, key, value, null)))
                        break;                   // no lock when adding to empty bin
                }
                else if ((fh = f.hash) == MOVED)
                    tab = helpTransfer(tab, f);
                else {
                    V oldVal = null;
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            if (fh >= 0) {   // Node 的 hash 值 肯定是正数,通过 spread(key.hashCode()) 方法得到的  无论是红黑树还是链表,hash 值都是正数,所以这个 if 恒成立呀
                                binCount = 1;
                                for (Node<K,V> e = f;; ++binCount) {
                                    K ek;
                                    if (e.hash == hash &&
                                        ((ek = e.key) == key ||
                                         (ek != null && key.equals(ek)))) {
                                        oldVal = e.val;
                                        if (!onlyIfAbsent)
                                            e.val = value;
                                        break;
                                    }
                                    Node<K,V> pred = e;
                                    if ((e = e.next) == null) {
                                        pred.next = new Node<K,V>(hash, key,
                                                                  value, null);
                                        break;
                                    }
                                }
                            }
                            else if (f instanceof TreeBin) {  // 对应上面 if TreeBin 继承自 Node Node 的 hashCode 是他的 key 和 value 的异或,这一点也奇怪 如果是看 Node 的 hash 的话,那更是都有可能了,怎么能这么搞
                                Node<K,V> p;
                                binCount = 2;
                                if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                               value)) != null) {
                                    oldVal = p.val;
                                    if (!onlyIfAbsent)
                                        p.val = value;
                                }
                            }
                        }
                    }
                    if (binCount != 0) {
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (oldVal != null)
                            return oldVal;
                        break;
                    }
                }
            }
            addCount(1L, binCount);
            return null;
        }
    
    
    4 条回复    2023-09-14 20:57:03 +08:00
    qwerthhusn
        1
    qwerthhusn  
       2023-09-14 08:42:39 +08:00
    因为那个 fh 是 Node 的 hash ,不是从 key 的 hashCode 中 spread 出来的值
    ConcurrentHashMap 中除了链表 Node 和树非根节点 TreeNode 的 hash 是正值并且来自 key 的 hashCode ,其余几种 Node 的 hash 都是负值,并且这个值不是从 Key 的 hashCode 来的,代表特殊 Node ,比如 ForwardingNode 代表在扩容过程中临时占位的 Node

    而 TreeNode 不会被 table 直接引用( table 会引用 TreeBin ),所以当 table 的某个 Node 为正值时肯定就是链表头部。
    Aresxue
        2
    Aresxue  
       2023-09-14 11:54:06 +08:00
    spread 出来的都是正值,但是节点的 hash 还有其它的几个状态:
    static final int MOVED = -1; // 代表当前 hash 位置的数据正在扩容!
    static final int TREEBIN = -2; // 代表当前 hash 位置下挂载的是一个红黑树
    static final int RESERVED = -3; // 预留当前索引位置
    weidalao
        3
    weidalao  
    OP
       2023-09-14 20:52:34 +08:00
    后面看到 TreeBin(TreeNode<K,V> b) {
    super(TREEBIN, null, null, null);
    this.first = b; 看到树化的时候,用了 node 构造方法,传了-2 过去了 感谢
    weidalao
        4
    weidalao  
    OP
       2023-09-14 20:57:03 +08:00
    @qwerthhusn 可以看 putVal 过程 无论是尾插 pred.next = new Node<K,V>(hash, key,
    value, null); 还是替换值 if (e.hash == hash &&
    ((ek = e.key) == key ||
    (ek != null && key.equals(ek)))) {
    oldVal = e.val; 都是拿的 spread 出来的 hash 值,只不过作为 TreeBin 的根节点 会特殊构造器赋值 hash 为-2 ,所以 本质 Node 的 hash 值 和 HashCode 是两个概念我搞混了 一个是都会有的 hashCode:public final int hashCode() { return key.hashCode() ^ val.hashCode(); } 一个是 int 类型 hash final int hash; 这个 hash 是没有计算的,直接赋值的 特殊用途
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1150 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:31 · PVG 02:31 · LAX 10:31 · JFK 13:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.