ConcurrentHashMap JDK 8 何时使用 computeIfPresent

jdk 8 的并发哈希映射的新版本有两个新的方法。

computeIfAbsent

computeIfPresent

putIfAbsent - 旧方法

我了解 putIfAbsentcomputeIfAbsent 的用例。但我不确定我何时将使用computeIfPresent的场景。另外,为什么我需要putIfAbsent,如果我现在有计算IfPresent。putIfAbsent 确实会创建至少一个额外的值实例。

原因只是为了有向后兼容性吗?


答案 1

正如在另一个答案中提到的:即使引入了新的,更强大的“方法”,也将始终保留方法以实现向后兼容性。

关于用例:可能很难找到一个足够小的例子,看起来不是人为的,但仍然令人信服。通常,此方法的目的是以任何形式更新现有值。computeIfPresent

一个例子可以是(受约束的)字数统计:对于一组给定的单词,可以在地图中存储初始字数。然后,处理一系列单词:每当从初始集合中找到一个单词时,其计数就会增加1:0

import java.util.LinkedHashMap;
import java.util.Map;

public class ComputeIfPresentExample 
{
    public static void main(String[] args) 
    {
        Map<String, Integer> wordCounts = new LinkedHashMap<String, Integer>();

        String s = 
            "Lorem ipsum dolor sit amet consetetur iam nonumy sadipscing " + 
            "elitr, sed diam nonumy eirmod tempor invidunt ut erat sed " + 
            "labore et dolore magna dolor sit amet aliquyam erat sed diam";

        wordCounts.put("sed", 0);
        wordCounts.put("erat", 0);

        for (String t : s.split(" "))
        {
            wordCounts.computeIfPresent(t, (k,v) -> v+1);
        }
        System.out.println(wordCounts);
    }
}

(当然,这样的事情可以以不同的方式解决,但这是一种或另一种形式的相当频繁的任务,新方法允许一个相当简洁和优雅的解决方案)


答案 2

一个常见的用例是带有集合地图,例如

Map<String, Collection<String>> strings = new HashMap<>();

computeIfAbsentcomputeIfPresent 是非常方便的操作,用于在集合中添加和删除元素。尤其是因为与 不同,这些方法返回当前值(无论它是否刚刚创建)。下面是一个按字符串的第一个字符对字符串进行分组的示例。请注意,密钥和集合都是在必要时创建的,并在集合变为空时进行清理:put()compute*()

void addString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfAbsent(index, ign -> new HashSet<>()).add(a);
}

void removeString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfPresent(index, (k, c) -> {
        c.remove(a);
        return c.isEmpty() ? null : c;
    });
}

例:

                         // {}
addString("a1");         // {a=[a1]}      <-- collection dynamically created
addString("a2");         // {a=[a1, a2]}
removeString("a1");      // {a=[a2]}
removeString("a2");      // {}            <-- both key and collection removed

这在多线程环境中非常强大,因为 ConcurrentMap 以原子方式执行这些操作。

删除操作可以是单行操作:

void removeString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfPresent(index, (i, c) -> c.remove(a) && c.isEmpty() ? null : c);
}

所以再一次简而言之:

Map<String, Set<String>> map = new ConcurrentHashMap<>();
map.computeIfAbsent(key, i -> ConcurrentHashMap.newKeySet()).add(value);
map.computeIfPresent(key, (i, s) -> s.remove(value) && s.isEmpty() ? null : s);

推荐