computeIfAbsent 和 putIfAbsent 都是在 Map 接口的实现类中用于有条件地插入键值对的方法,但它们之间有重要的区别。下面是关于这两个方法的详细对比:
putIfAbsent(key, value)
功能:尝试将指定的值与指定的键关联,但仅当键尚未映射到值时才这样做。
参数:键和要插入的值。
返回值:返回与键先前关联的值,或者如果键没有映射到任何值,则返回 null。
使用场景:当你想要在 Map 中插入一个值,但只有当键不存在时才这样做,以避免覆盖现有的值。
computeIfAbsent(key, mappingFunction)
功能:如果键尚未映射到值,则应用给定的函数 mappingFunction 来计算值,并将其与键关联。
参数:键和一个函数 mappingFunction,该函数接受键作为参数并返回一个值。
返回值:返回与键关联的值,如果键不存在,则返回由 mappingFunction 计算出的值。
使用场景:当你需要在键不存在时计算一个值,并且这个计算可能是昂贵的或依赖于键的,使用 computeIfAbsent 可以确保计算只在必要时发生,并且计算的结果是线程安全地存储在 Map 中。
区别总结
计算值:putIfAbsent 直接使用提供的值,而 computeIfAbsent 使用一个函数来计算值。
资源利用:如果值的计算成本高,computeIfAbsent 更加节省资源,因为它仅在键不存在时才计算值。
灵活性:computeIfAbsent 提供了更多的灵活性,因为你可以根据键动态计算值。
线程安全:在 ConcurrentMap 实现中,computeIfAbsent 是原子操作,确保了线程安全,而 putIfAbsent 在某些情况下也可能需要额外的同步措施,尤其是在复杂的并发场景中。
// 使用 putIfAbsent
Map<String, Integer> map = new HashMap<>();
Integer oldValue = map.putIfAbsent("key", 1);
if (oldValue == null) {
System.out.println("Value inserted.");
} else {
System.out.println("Value already exists: " + oldValue);
}
// 使用 computeIfAbsent
ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
Integer value = concurrentMap.computeIfAbsent("key", k -> {
// 如果 "key" 不存在,计算一个值
return 0;
});
返回值差异
computeIfAbsent(key, mappingFunction)
返回值:返回与给定键关联的最终值。如果键之前没有映射到任何值,则返回由 mappingFunction 计算出的新值;如果键已经映射到一个值,则返回该现有值。
设计目的:确保键对应的值要么是已存在的,要么是通过提供的函数计算得出的,从而避免了不必要的重复计算。
putIfAbsent(key, value)
返回值:返回与给定键之前关联的值,或者如果键没有映射到任何值,则返回 null。(旧值)
设计目的:允许在 Map 中有条件地放置一个键值对,只有当键尚未被映射时才进行放置。这有助于避免覆盖已有的值。