【问题标题】:ConcurrentHashMap JDK 8 when to use computeIfPresentConcurrentHashMap JDK 8 何时使用 computeIfPresent
【发布时间】:2014-07-21 18:52:56
【问题描述】:

jdk 8的新版Concurrent Hash Map新增了两个Method。

computeIfAbsent

computeIfPresent

putIfAbsent - 旧方法

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

仅仅是为了向后兼容吗?

【问题讨论】:

    标签: java multithreading concurrency concurrenthashmap


    【解决方案1】:

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

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

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

    【讨论】:

    • computeIfPresent 的另一个好处:BiFunction 的 value 参数确保不为空。
    【解决方案2】:

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

    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
    

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

    删除操作可以是单行的:

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

    【讨论】:

    • 这是computeIfPresent的真正用例。
    【解决方案3】:

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

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

    【讨论】:

    • @thriller 哦,我知道它必须是向后兼容的。我的问题是唯一的原因吗?以及 computeIfPresent 的用例。
    猜你喜欢
    • 1970-01-01
    • 2017-06-14
    • 2014-09-12
    • 2016-01-02
    • 2019-03-25
    • 1970-01-01
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多