简体   繁体   English

无法理解在ConcurrentHashMap中重写equals方法的实现的优势

[英]Cant understand advantage of overridden implementation of equals method in ConcurrentHashMap

Most of the map classes in Java override AbstractMap and use its implementation of equals method which checks that: Java中的大多数map类都覆盖了AbstractMap并使用equals方法的实现来检查:

  1. passed object is of type Map 传递的对象是Map类型
  2. has same length 长度相同
  3. contains all the entries present in this 包含所有存在于条目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; 

But ConcurrentHashMap uses a different implementation where instead of matching length of both the maps, the entries present in passed map are also iterated and matched. 但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;

Since equals method is not thread safe even in ConcurrentHashMap can someone suggest what is the benefit of skipping length check and instead iterating and matching entries from passed object? 由于equals方法即使在ConcurrentHashMap也不是线程安全的,有人可以建议跳过长度检查的好处是什么,而是迭代和匹配来自传递对象的条目?

As pointed in answers below that size is not available as a direct field, this is the equals implementation which I believe is more efficient. 如下面的答案所指出的那样,大小不能作为直接字段,这是我认为更有效的equals实现。 Please clarify issues in this one. 请澄清这一问题。 Mostly we are not doing any lookup in the last loop. 大多数情况下,我们没有在最后一个循环中进行任何查找。

    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;

I think that checking the size would be useless, when computing the size Traverser is not used at all, it uses a specialization of LongAdder (called CounterCell ), so it takes time to compute the size and by the time this is done - the CHM could change entirely before traversing. 我认为检查大小是没用的,当计算Traverser的大小根本没用时,它使用了LongAdder的专门化(称为CounterCell ),因此需要时间来计算大小和完成时间 - CHM在遍历之前可以完全改变。

Even computing the size has no guarantees that it will be correct for example CHM could be mutated while computing the size - so that number would not be accurate. 即使计算size也无法保证它是正确的,例如计算大小 CHM可能会发生变异 - 因此数字不准确。

So I guess this can be seen as an optimization: why compute the size if most of the time it is useless anyway. 所以我猜这可以看作是一种优化:为什么计算尺寸,如果大部分时间它都是无用的。

The implementation of ConcurrentHashMap.size() is quite different from most other Map s. ConcurrentHashMap.size()的实现与大多数其他Map完全不同。

HashMap and TreeMap implement it by just returning the value of a private field size they maintain anyway. HashMapTreeMap通过返回它们维护的私有字段size的值来实现它。

ConcurrentHashMap does not maintain such a field. ConcurrentHashMap不维护这样的字段。 This would be difficult to implement in a non-blocking way while still allowing concurrent modifications to distinct buckets to interfere as little as possible with each other. 这将难以以非阻塞方式实现,同时仍然允许对不同桶的并发修改以尽可能少地彼此干扰。

The Java 8 version I'm looking at has the following implementation 我正在看的Java 8版本具有以下实现

/**
 * {@inheritDoc}
 */
public int size() {
    long n = sumCount();
    return ((n < 0L) ? 0 :
            (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
            (int)n);
}

where sumCount() iterates an array of counterCells . 其中sumCount()迭代一个counterCells数组。

This is a lot less attractive as a quick check than a simple field access. 与简单的现场访问相比,这种快速检查的吸引力要小得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM