Java 中的大多数 map 类都会覆盖 AbstractMap
并使用其实现的 equals
方法来检查:
- 传递的对象是Map类型
- 长度相同
包含
中的所有条目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/