问题
HashMap方法putIfAbsent如何能够有条件地以比事先调用containsKey(x)更快的方式有条件地执行看跌期权?
例如,如果您不使用putIfAbsent,则可以使用:
if(!map.containsKey(x)){
map.put(x,someValue);
}
我以前曾以为putIfAbsent是调用containsKey之后在HashMap上放置put的便捷方法。但是运行基准测试后,putIfAbsent的速度明显比使用containsKey和Put的速度快。我查看了java.util源代码,尝试尝试这是怎么实现的,但对于我来说太神秘了。有谁内部知道putIfAbsent在更好的时间复杂度下如何工作?那就是我的假设,基于运行一些代码测试得出的,使用putIfAbsent时我的代码运行速度提高了50%。似乎避免调用get(),但是怎么做呢?
示例
if(!map.containsKey(x)){
map.put(x,someValue);
}
VS
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;
}
最佳答案
HashMap
的putIfAbsent
实现只搜索一次 key ,如果找不到该 key ,则将值放入相关的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
的性能相同,这比同时调用containsKey
和put
更快。