Java ConcurrentHashMap JDK 8 何时使用 computeIfPresent

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24872531/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 15:19:06  来源:igfitidea点击:

ConcurrentHashMap JDK 8 when to use computeIfPresent

javamultithreadingconcurrencyconcurrenthashmap

提问by veritas

The new version of Concurrent Hash Map of jdk 8 has two new Methods.

jdk 8 新版本的 Concurrent Hash Map 新增了两个方法。

computeIfAbsent

计算如果不存在

computeIfPresent

计算如果存在

putIfAbsent- Old method

putIfAbsent- 旧方法

I understand the use cases of putIfAbsentand computeIfAbsent. But i am not sure of the scenarios when i will use computeIfPresent. Also why do i need putIfAbsent if i have computeIfPresent now. putIfAbsent do create atleast one extra instance of the value.

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

Is the reason is only to have backward compatability?

难道只是为了向后兼容?

采纳答案by Marco13

As mentioned in the other answer: Methods will always be kept for backward compatibility, even if there are new, more "powerful" methods introduced.

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

Concerning the use case for computeIfPresent: It may be hard to find an example that is small enough to not look contrived and still be convincing. In general, the intention of this method is to updatean existing value in any form.

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

One example could be a (constrained) word count: For a given set of words, one stores an initial count of 0in the map. Then, a sequence of words is processed: Whenever one finds a word from the initial set, its count is increased by 1:

一个例子可能是(受约束的)字数:对于给定的一组字,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);
    }
}

(Of course, things like this could be solved differently, but this is a rather frequent task in one or the other form, and the new method allows a rather concise and elegant solution)

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

回答by Thirler

The JDK hardly ever breaks backwards compatibility. Because then you can not easily port or run software from an older version with the latest version.

JDK 几乎不会破坏向后兼容性。因为那样你就不能轻易地从旧版本移植或运行最新版本的软件。

You can run software compiled with an older version of the library with any version (meaning users that have the JRE installed) that still has those functions.

您可以运行使用旧版本库编译的软件,任何版本(意味着安装了 JRE 的用户)仍然具有这些功能。

回答by austin327

I've used computeIfPresent as a null-safe way to fetch lowercase values from a map of strings.

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

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

Before computeIfPresent was available I'd have to do this:

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

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

Or this:

或这个:

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

回答by steffen

A common use case are mapswith collections, like

一个常见的用例是带有集合的映射,例如

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

computeIfAbsentand computeIfPresentare very handy operations for adding and removing elements to/from the collection. Not least because unlike put(), the compute*()methods return the current value (whether it was just created or not). Here's an example that groups strings by their first char. Note that both the keys and the collections are created when necessary and cleaned it up when the collection becomes empty:

computeIfAbsent并且computeIfPresent是在集合中添加和删除元素的非常方便的操作。尤其是因为与 不同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;
    });
}

Example:

例子:

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

This is extremely powerful in multithreading environments as ConcurrentMapsperform these operations atomically.

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

The remove operation can be a one-liner:

删除操作可以是单行的:

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

So once again in short:

所以再次简而言之:

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