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

时间:2018-06-14 12:12:06

标签: java java.util.concurrent

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

  1. 传递的对象是Map
  2. 类型
  3. 长度相同
  4. 包含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;
    
  5. 但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;
    

2 个答案:

答案 0 :(得分:2)

我认为检查大小是没用的,当计算大小LongAdder根本没用时,它使用CounterCell的专门化(称为size),所以它需要计算尺寸的时间和完成时间 - 在遍历之前CHM可以完全改变。

即使计算CHM也不能保证它是正确的,例如{{1}}可以在计算大小时突变 - 因此该数字不准确。

所以我猜这可以看作是一种优化:为什么在大多数情况下计算尺寸无论如何都是无用的。

答案 1 :(得分:1)

ConcurrentHashMap.size()的实施与大多数其他Map完全不同。

HashMapTreeMap只需返回他们维护的私有字段size的值即可实现。

ConcurrentHashMap没有维持这样的字段。这将难以以非阻塞方式实现,同时仍然允许对不同桶的并发修改以尽可能少地彼此干扰。

我正在查看的Java 8版本具有以下实现

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

其中sumCount()迭代一个counterCells数组。

与简单的现场访问相比,这不像快速检查那么有吸引力。