ConcurrentHashMap JDK 8何时使用computeIfPresent

时间:2014-07-21 18:52:56

标签: java multithreading concurrency concurrenthashmap

jdk 8的新版Concurrent Hash Map有两个新方法。

computeIfAbsent

computeIfPresent

putIfAbsent - 旧方法

我了解 putIfAbsent computeIfAbsent 的用例。 但我不确定我将使用 computeIfPresent 的情况。 另外,如果我现在有computeIfPresent,为什么还需要putIfAbsent。 putIfAbsent会创建至少一个额外的值实例。

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

4 个答案:

答案 0 :(得分:31)

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

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

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

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);
    }
}

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

答案 1 :(得分:5)

一个常见的用例是mapscollections,例如

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

computeIfAbsentcomputeIfPresent是向集合添加元素或从集合中删除元素的非常方便的操作。这是一个通过第一个字符串对字符串进行分组的示例。请注意,必要时会创建键和集合,并在集合变空时将其清除:

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

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

删除操作可以是一行代码:

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

答案 2 :(得分:0)

JDK几乎没有打破向后兼容性。因为那时您无法轻松地从具有最新版本的旧版本移植或运行软件。

您可以运行使用旧版本库编译的软件,其中包含仍具有这些功能的任何版本(即安装了JRE的用户)。

答案 3 :(得分:-1)

我使用computeIfPresent作为从字符串映射中获取小写值的空安全方法。

String s = fields.computeIfPresent("foo", (k,v) -> v.toLowerCase())

在computeIfPresent可用之前,我必须这样做:

String s = map.get("foo");
if (s != null) {
    s = s.toLowerCase();
}

或者这个:

String s = map.containsKey("foo") ? map.get("foo").toLowerCase() : null;