Java中的大多数地图类覆盖AbstractMap
并使用其equals
方法的实现来检查:
包含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;
答案 0 :(得分:2)
我认为检查大小是没用的,当计算大小LongAdder
根本没用时,它使用CounterCell
的专门化(称为size
),所以它需要计算尺寸的时间和完成时间 - 在遍历之前CHM可以完全改变。
即使计算CHM
也不能保证它是正确的,例如{{1}}可以在计算大小时突变 - 因此该数字不准确。
所以我猜这可以看作是一种优化:为什么在大多数情况下计算尺寸无论如何都是无用的。
答案 1 :(得分:1)
ConcurrentHashMap.size()
的实施与大多数其他Map
完全不同。
HashMap
和TreeMap
只需返回他们维护的私有字段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
数组。
与简单的现场访问相比,这不像快速检查那么有吸引力。