Java 并发hashmap size()方法复杂性

Java 并发hashmap size()方法复杂性,java,concurrency,java.util.concurrent,concurrenthashmap,Java,Concurrency,Java.util.concurrent,Concurrenthashmap,我想知道在ConcurrentHashMap上调用的size()方法是否与普通HashMap的size()方法具有相同的复杂性 不是。在我的JDK版本中,HashMap.size()具有O(1)复杂性,而ConcurrentHashMap.size()在最好的情况下必须迭代段。在最坏的情况下,它将锁定所有段,这在多线程场景中可能是一个非常昂贵的操作 当然,哪个更快是完全不同的问题。答案很大程度上取决于有多少线程正在访问映射,以及它们到底在做什么。HashMap上的size()的复杂性是O(1),

我想知道在
ConcurrentHashMap
上调用的
size()
方法是否与普通HashMap的
size()
方法具有相同的复杂性

不是。在我的JDK版本中,
HashMap.size()
具有
O(1)
复杂性,而
ConcurrentHashMap.size()
在最好的情况下必须迭代段。在最坏的情况下,它将锁定所有段,这在多线程场景中可能是一个非常昂贵的操作


当然,哪个更快是完全不同的问题。答案很大程度上取决于有多少线程正在访问映射,以及它们到底在做什么。

HashMap上的
size()
的复杂性是
O(1)
,因为大小存储在字段中。如果我们看一下
ConcurrentHashMap
size()
的实现,就会发现它更大(>O(1))

参考(openjdk-6)

正如您在源代码中看到的,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;
}
/**
*返回此映射中的键值映射数。如果
*映射包含多个Integer.MAX\u值元素,返回
*Integer.MAX_值。
*
*@返回此映射中的键值映射数
*/
公共整数大小(){
最终段[]段=本段;
长和=0;
长检查=0;
int[]mc=新的int[segments.length];
//尝试几次以获得准确的计数。由于
//表中的连续异步更改,求助于锁定。
for(int k=0;k<锁定前重试;++k){
检查=0;
总和=0;
int mcsum=0;
对于(int i=0;i整数最大值)
返回Integer.MAX_值;
其他的
返回(整数)和;
}

这是一个无关紧要的问题,因为在并发可变结构上调用size()实际上是一件毫无意义的事情。它所能告诉你的只是它的大小,除了记录它之外,你不能用这些信息做任何事情

如果您尝试在不锁定结构的情况下使用size(),您将遇到竞争条件。

JDK 8的新实现使用了一种很酷的算法,这种算法类似于复制粘贴


实际上,
ConcurrentHashMap.size()
的复杂性几乎是恒定的(在nerd语言中是“O(1)”),与
HashMap.size()相比,实时成本可以忽略不计。不相信我?打开我的basic,自己跑步。我当前的机器上没有安装JDK 7,与Java 1.8相比,使用Java 1.7获得关于时间成本的反馈会很酷。

有趣的发现,Martin!我发现在JDK 8中,这不再是事实。请在本页(或)的其他地方查看我的答案。这并没有真正的帮助。您是否可以删除较长的源代码片段,并告诉我们为什么映射被拆分为多个段,以及我们希望找到多少段?只有当分段数为N的常数分数时,我们才能得到O(N)复杂度。