2017-08-02 71 views
11

Java8介绍那些好的方法getOrDefault()putIfAbsent(),让写代码,如:我应该在使用getOrDefault()之后使用put()或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可能做了很多不必要的“放”呼叫时将元素添加到列表经常发生
  • 选项2可能做了很多不必要的“的containsKey”的呼吁时添加新条目,新密钥是主导

SO:是选择1还是选项2“总是”的好理由?

+13

Ahem,*既不*。对于整个操作,使用'itemsByFoo.computeIfAbsent(key,x - > new ArrayList <>()).add(someNeBar);'。 – Holger

+0

@Holger是:)极好的一点。因为'putIfAbsent'可能会返回'null',因为它返回* previous *值...此外'computeifAbsent'存在于java-8中,而不是7个。我之前遇到过这种情况...... – Eugene

+2

@Eugene:'putIfAbsent'被添加到Java 8的'Map'接口中,因为现在可以使用'default'方法,但它必须保留自Java 5以来存在的'ConcurrentMap.putIfAbsent'的协定,所以它不像'computeIfAbsent'那样方便...... – Holger

回答

19

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只能执行一次。

+0

不是'itemsFoo.compute ...; bars.add(someNewBar)现在是一种竞争状态?因为它不是一个单一的原子操作;有人可以在“add”完成时删除该条目?我想知道我今天是否在耐心的限制中遇到如此多的以下问题... – Eugene

+5

@Eugene:这是一个普通的'Map'问题。原子是不是一个要求。否则,你还有很多事情要做。虽然你可以使插入线程安全地完成'compute'中的所有操作,但它与最终读取'List'的代码没有任何关系,并且必须有代码读取它,如果存储本身不是目的,那么对于任何现实生活中的情况,无论如何都需要额外的努力。 – Holger

+0

谢谢。我在这里看到'CHM'在哪里?我的错。对于'putIfAbsent'来说,这是一个很好的观点,它在默认实现中执行了2次查找......我注意到它现在只是“get”,然后是“put”。这将是一个很好的例子,说明为什么默认方法被覆盖。 – Eugene

5

computeIfAbsent重要的一点是,它需要将只能得到执行,如果Key不存在一个Function,我们需要一个默认Value

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

我们希望推迟这样做,直到我们确信key尚未在itemsByFoo中。否则我们会为gc收集产生不必要的垃圾。

+0

优秀的附加信息;-) – GhostCat