java - 是什么让 Hashmap.putIfAbsent 比 containsKey 后接 put 更快?

标签 java collections hashmap

问题

HashMap 方法 putIfAbsent 如何能够以比之前调用 containsKey(x) 更快的方式有条件地执行放置?

例如,如果您不使用 putIfAbsent,您可以使用:

 if(!map.containsKey(x)){ 
   map.put(x,someValue); 
}

我之前认为 putIfAbsent 是调用 containsKey 后跟一个 HashMap 的便捷方法。但在运行基准测试后,putIfAbsent 比使用 containsKey 后跟 Put 快得多。我查看了 java.util 源代码以尝试了解这是如何实现的,但它对我来说有点太神秘了,无法弄清楚。有谁在内部知道 putIfAbsent 似乎如何在更好的时间复杂度下工作?这是我基于运行一些代码测试的假设,其中使用 putIfAbsent 时我的代码运行速度提高了 50%。似乎避免调用 get() 但如何避免?

示例

if(!map.containsKey(x)){
     map.put(x,someValue);
}

对比

map.putIfAbsent(x,somevalue)

Hashmap.putIfAbsent 的 Java 源代码

@Override
public V putIfAbsent(K key, V value) {
    return putVal(hash(key), key, value, true, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

最佳答案

putIfAbsentHashMap 实现只搜索一次键,如果没有找到键,它会将值放入相关的 bin(这是已经定位)。这就是 putVal 所做的。

另一方面,使用 map.containsKey(x) 后跟 map.put(x,someValue) 中的键执行两次查找 map ,这需要更多的时间。

请注意,put 还调用了 putVal(put 调用了 putVal(hash(key), key, value, false, true )putIfAbsent 调用 putVal(hash(key), key, value, true, true)), 所以 putIfAbsent 有与仅调用 put 的性能相同,这比同时调用 containsKeyput 更快。​​

关于java - 是什么让 Hashmap.putIfAbsent 比 containsKey 后接 put 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52511013/

相关文章:

java - 创建可执行的java maven项目

java - 如何设置并显示本地数据库中的另一个字段?

java - 处理 TCP 客户端断开连接

java - 按值字段搜索 map

java - 这段代码有什么作用? (Java 集合)

java - 如果我在同步块(synchronized block)中使用等待,则 IllegalMonitorStateException 的原因

VBA集合: list of keys

java - 根据 Object 的成员变量从值对 HashMap 进行排序

java - 从 HashMap<String, ArrayList<String>> 获取值

java - 使用可迭代到 Hashmap 的迭代器实现 - 可能吗?