java - 无法理解在 ConcurrentHashMap 中重写 equals 方法的优势

标签 java java.util.concurrent

Java 中的大多数 map 类都会覆盖 AbstractMap 并使用其实现的 equals 方法来检查:

  1. 传递的对象是Map类型
  2. 长度相同
  3. 包含this

    中的所有条目
    if (o == this)
        return true;
    
    //check that passed object is of type Map
    if (!(o instanceof Map))
        return false;
    Map<?,?> m = (Map<?,?>) o;
    
    //check that passed object has same length
    if (m.size() != size())
        return false;
    
    //passed object contains all the entries
    try {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            if (value == null) {
                if (!(m.get(key)==null && m.containsKey(key)))
                    return false;
            } else {
                if (!value.equals(m.get(key)))
                    return false;
            }
        }
    } catch (ClassCastException unused) {
        return false;
    } catch (NullPointerException unused) {
        return false;
    }
    
    return true;
    

但 ConcurrentHashMap 使用不同的实现方式,不是匹配两个映射的长度,而是迭代和匹配传递的映射中存在的条目。

    if (o != this) {

        //check that passed object is of type Map
        if (!(o instanceof Map))
            return false;
        Map<?,?> m = (Map<?,?>) o;
        Node<K,V>[] t;
        int f = (t = table) == null ? 0 : t.length;
        Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);

        //passed object contains all the entries
        for (Node<K,V> p; (p = it.advance()) != null; ) {
            V val = p.val;
            Object v = m.get(p.key);
            if (v == null || (v != val && !v.equals(val)))
                return false;
        }

        //this contains all the entries of the passed object
        for (Map.Entry<?,?> e : m.entrySet()) {
            Object mk, mv, v;
            if ((mk = e.getKey()) == null ||
                (mv = e.getValue()) == null ||
                (v = get(mk)) == null ||
                (mv != v && !mv.equals(v)))
                return false;
        }
    }
    return true;

由于 equals 方法即使在 ConcurrentHashMap 中也不是线程安全的,有人可以建议跳过长度检查并迭代和匹配传递对象的条目有什么好处吗?

正如下面的答案所指出的那样,大小不能作为直接字段使用,这是我认为更有效的 equals 实现。请澄清这一个中的问题。大多数情况下,我们不会在最后一个循环中进行任何查找。

    if (o != this) {

        //check that passed object is of type Map
        if (!(o instanceof Map))
            return false;
        Map<?,?> m = (Map<?,?>) o;
        Node<K,V>[] t;
        int f = (t = table) == null ? 0 : t.length;
        Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
        int thisSize=0;

        //passed object contains all the entries
        for (Node<K,V> p; (p = it.advance()) != null; ) {
            V val = p.val;
            Object v = m.get(p.key);
            if (v == null || (v != val && !v.equals(val)))
                return false;
            thisSize++;
        }

        //passed object is of the same size, ignoring any modifications since invocation of equals
        int passedObjectSize=0;
        for (Map.Entry<?,?> e : m.entrySet()) {
            Object mk, mv, v;
            if ((mk = e.getKey()) == null ||
                (mv = e.getValue()) == null){
                return false;
            }
            //ignore checking that get(mk) is same as mv
            passedObjectSize++;
        }
        return thisSize==passedObjectSize;
    }
    return true;

最佳答案

我认为检查大小是没有用的,当计算大小时 Traverser 根本没有被使用,它使用了 LongAdder 的特化(称为 CounterCell ),因此计算大小需要时间,并且在计算完成时 - CHM 可能会在遍历之前完全更改。

即使计算 size 也不能保证它是正确的,例如 CHM 可以在 计算大小时发生变化 - 所以这个数字会不准确。

所以我想这可以看作是一种优化:如果大多数时候无论如何都没有用,为什么要计算大小。

关于java - 无法理解在 ConcurrentHashMap 中重写 equals 方法的优势,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50857280/

相关文章:

java - 使用线程池时出现错误 FutureTask@2c7b84de 被拒绝

java - 如何在 Concurrenthashmap 中做弱引用

java - 为什么java中线程通信不起作用?

java - Java中如何实现线程安全的HashMap取值时延迟初始化?

java - 错误: int cannot be dereferenced java (repeat)

java - 如何使用java递归获取JSON的键?

java - url 采集器并发问题,ConcurrentModificationException

java - 不可能(?): NullPointerException on ConcurrentLinkedQueue. 大小()

java - 如何反向选择sql中的列?

java - <h :selectOneMenu> NullPointerException getAsObject method