我想知道在size()
上调用的ConcurrentHashMap
方法与通常的HashMap的size()
方法的复杂性是否相同。
答案 0 :(得分:6)
JDK 8 中ConcurrentHashMap.size()的新实现使用了一种很酷的算法,它们可以从LongAdder复制粘贴。
实际上,ConcurrentHashMap.size()
的复杂性几乎是不变的(" O(1)"用书呆子语言),与HashMap.size()
相比,实时成本可以忽略不计。别相信我?打开我的基本test project并自己跑步。我没有在我当前的机器上安装JDK 7,与Java 1.8相比,使用Java 1.7获得时间成本反馈会很酷。
答案 1 :(得分:5)
不是。在我的JDK版本中,HashMap.size()
具有O(1)
复杂度,而ConcurrentHashMap.size()
在最佳情况下必须迭代这些段。在最坏的情况下,它将锁定所有段,这在多线程场景中可能是相当昂贵的操作。
当然,更快是一个完全不同的问题。答案很大程度上取决于访问地图的线程数量,以及它们正在做什么。
答案 2 :(得分:5)
size()
上HashMap
的复杂性为O(1)
,因为大小存储在字段中。如果我们在size()
上查看ConcurrentHashMap
的实现,我们会看到它更大(> O(1))
参考(openjdk-6)
答案 3 :(得分:0)
ConcurrentHashMap中size()方法的复杂性本质上是一个O(N)操作(主要根据段的数量而变化),如您在源代码中看到的那样。 HashMap是一个O(1)操作。
/**
* Returns the number of key-value mappings in this map. If the
* map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of key-value mappings in this map
*/
public int size() {
final Segment<K,V>[] segments = this.segments;
long sum = 0;
long check = 0;
int[] mc = new int[segments.length];
// Try a few times to get accurate count. On failure due to
// continuous async changes in table, resort to locking.
for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
check = 0;
sum = 0;
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
sum += segments[i].count;
mcsum += mc[i] = segments[i].modCount;
}
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
check += segments[i].count;
if (mc[i] != segments[i].modCount) {
check = -1; // force retry
break;
}
}
}
if (check == sum)
break;
}
if (check != sum) { // Resort to locking all segments
sum = 0;
for (int i = 0; i < segments.length; ++i)
segments[i].lock();
for (int i = 0; i < segments.length; ++i)
sum += segments[i].count;
for (int i = 0; i < segments.length; ++i)
segments[i].unlock();
}
if (sum > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
else
return (int)sum;
}
答案 4 :(得分:0)
这是一个无关紧要的问题,因为在同时可变的结构上调用size()是一件毫无意义的事情。所有它会告诉你的是是的大小,除了记录它之外,你实际上无法对这些信息做任何事情。
如果您尝试使用size()而不锁定结构,则会出现竞争条件。