前言
在Java 8中,Map接口引入了一些新的默认方法,这些方法极大地增强了Map的功能。其中,computeIfAbsent和putIfAbsent是用于处理缺失键值对的两种常用方法。它们在某些情况下可以避免常见的并发问题,提高代码的效率和可读性。
putIfAbsent 方法
putIfAbsent方法用于在Map中只有在指定键不存在时才插入指定的值。它不会覆盖已经存在的键对应的值。
示例代码如下:
import java.util.HashMap;
import java.util.Map;
public class PutIfAbsentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
// 使用 putIfAbsent 插入值
// 由于 "key1" 已经存在,所以不会插入 "newValue1"
map.putIfAbsent("key1", "newValue1");
// "key2" 不存在,所以插入 "value2"
map.putIfAbsent("key2", "value2");
// 输出: {key1=value1, key2=value2}
System.out.println(map);
}
}
源码剖析
我们来看一下putIfAbsent方法的JDK源码(以HashMap为例):
public V putIfAbsent(K key, V value) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null) {
e = putVal(hash(key), key, value, true, true);
}
return e == null ? null : e.value;
}
在这个方法中,首先使用getNode方法检查给定键是否已经存在于Map中。如果不存在,就调用putVal方法插入新的键值对。
computeIfAbsent 方法
computeIfAbsent方法用于在Map中如果指定的键不存在,则使用给定的映射函数计算其值,并将该值插入Map中。与putIfAbsent不同的是,computeIfAbsent能够动态计算要插入的值。
示例代码如下:
import java.util.HashMap;
import java.util.Map;
public class ComputeIfAbsentExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
// 使用 computeIfAbsent 动态计算值
map.computeIfAbsent("key1", k -> k.length());
map.computeIfAbsent("key2", k -> k.length());
// 输出: {key1=4, key2=4}
System.out.println(map);
}
}
源码分析
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
if (mappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V> e;
V oldValue;
if ((e = getNode(hash, key)) == null) {
V v;
if ((v = mappingFunction.apply(key)) != null) {
e = putVal(hash, key, v, false, true);
}
}
return e == null ? null : e.value;
}
在computeIfAbsent中,如果给定键不存在,则通过映射函数mappingFunction计算新值,并调用putVal方法将其插入Map。
两者的区别
功能差异:
- putIfAbsent仅在键不存在时插入给定的值。computeIfAbsent在键不存在时通过一个映射函数计算并插入新值。
使用场景:
- 当你有一个确定的默认值并希望在键缺失时插入时,使用putIfAbsent。当值需要根据键动态计算时,使用computeIfAbsent。
性能:
- computeIfAbsent由于需要执行映射函数,可能会略慢于putIfAbsent,但在需要动态计算值的场景下,这是必需的。
在Java的开发过程中,合理选择putIfAbsent和computeIfAbsent可以简化代码逻辑,减少错误。它们帮助开发者更方便地处理缺失值的情况,特别是在需要考虑并发和性能时。