【问题标题】:Should I use put() or putIfAbsent() after using getOrDefault()?使用 getOrDefault() 后应该使用 put() 还是 putIfAbsent()?
【发布时间】:2018-01-09 13:42:59
【问题描述】:

Java8 引入了getOrDefault()putIfAbsent() 这两个不错的方法,允许编写如下代码:

Map<Foo, List<Bar>> itemsByFoo = ...
List<Bar> bars = itemsByFoo.getOrDefault(key, new ArrayList<>());
bars.add(someNewBar);

现在我想知道是否有充分的事实理由这样做:

itemsByFoo.put(key, bars);

itemsByFoo.putIfAbsent(key, bars);

两者都可以:

  • 向列表中添加元素经常发生时,选项 1 可能会执行很多不必要的“放置”调用
  • 为新键添加新条目占主导地位时,option2 可能会执行很多不必要的“containsKey”调用

所以:选择选项 1 或选项 2“总是”的充分理由是什么?

【问题讨论】:

  • 咳咳,都没有。整个操作使用itemsByFoo.computeIfAbsent(key, x -&gt; new ArrayList&lt;&gt;()) .add(someNewBar);
  • @Holger 是的 :) 好点。因为putIfAbsent 可能会返回null,因为它会返回previous 值...而且computeifAbsent 存在于java-8 中,而不是7。我之前遇到过这个问题...
  • @Eugene: putIfAbsent 被添加到 Java 8 中的 Map 接口中,因为现在可以使用 default 方法,但它必须保持 ConcurrentMap.putIfAbsent 的合同,这从 Java 5 开始就存在了,所以不如computeIfAbsent...
  • 在这两种情况下都会调用 putVal 函数并执行以下代码 p = tab[i = (n - 1) & hash 。 p.Key 是键的值。将此键与您提供的要放入 HashMap 的键值进行比较。在这两种情况下 contains 无论如何都称为

标签: java dictionary collections java-8


【解决方案1】:

getOrDefault 适用于如果您想在不修改映射的情况下使用替代值来代替缺失值。如果要为缺少的键添加新值,只需一次操作即可完成。

List<Bar> bars = itemsByFoo.computeIfAbsent(key, x -> new ArrayList<>());
bars.add(someNewBar);

甚至

itemsByFoo.computeIfAbsent(key, x -> new ArrayList<>()).add(someNewBar);

在最好的情况下,当被Map 实现覆盖时,就像HashMap 一样,这将只进行一次哈希查找。

并不是说putIfAbsent 在使用default 实现时只进行两次查找,但是当然,大多数Map 实现将为它提供一个查找实现。尽管如此,getOrDefaultputIfAbsent 的组合在最好的情况下仍然可以进行两次查找,而优化的computeIfAbsent 只能进行一次。

【讨论】:

  • itemsFoo.compute...; bars.add(someNewBar) 现在不是竞争条件吗?因为它不是一个单一的原子操作;有人可以在add 完成时删除该条目吗?我想知道我今天是否有这么多以下问题达到了你的耐心极限......
  • @Eugene:这是一个通用的Map 问题。成为原子不是必需的。否则,你还有很多事情要做。虽然您可以使插入线程安全地在compute 内执行所有操作,但它与最终读取List 的代码无关,并且如果存储本身不是终点,则必须有代码读取它,所以对于任何现实生活中的案例,无论如何都需要付出额外的努力。
  • 谢谢。我在世界哪里看到CHM?我的错。关于putIfAbsent,这是一个很好的观点,它在默认实现中进行了2次查找......我刚才注意到它先是get,然后是put。这将成为一个很好的例子,说明为什么默认方法会被覆盖。
【解决方案2】:

关于computeIfAbsent 的重要一点是它需要一个Function,它只有在Key 不存在时才会被执行,我们需要一个默认的Value

getOrDefault 需要默认的 Value 本身,已计算。在这种情况下,我们需要的默认Valuenew ArrayList&lt;Bar&gt;(),它具有在堆上分配新对象的副作用。

我们想推迟这样做,直到我们确定key 不在itemsByFoo 中。否则我们会为gc 收集不必要的垃圾。

【讨论】:

    猜你喜欢
    • 2015-06-03
    • 1970-01-01
    • 2014-11-06
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 2018-09-05
    相关资源
    最近更新 更多