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

239 天前
 weidalao

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;
    }

854 次点击
所在节点    Java
4 条回复
qwerthhusn
238 天前
因为那个 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
238 天前
spread 出来的都是正值,但是节点的 hash 还有其它的几个状态:
static final int MOVED = -1; // 代表当前 hash 位置的数据正在扩容!
static final int TREEBIN = -2; // 代表当前 hash 位置下挂载的是一个红黑树
static final int RESERVED = -3; // 预留当前索引位置
weidalao
238 天前
后面看到 TreeBin(TreeNode<K,V> b) {
super(TREEBIN, null, null, null);
this.first = b; 看到树化的时候,用了 node 构造方法,传了-2 过去了 感谢
weidalao
238 天前
@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 是没有计算的,直接赋值的 特殊用途

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/973547

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX