映射getOrDefault VS getOrUseSupplier

时间:2016-04-20 11:22:01

标签: java lambda functional-programming

我开始学习lambdas,我不明白为什么java Map有:

getOrDefault(Object key, V defaultValue)

而不是(工作方式相同,但如果没有值,那么将从供应商处获取defaultValue):

getOrUseSupplier(Object key, Supplier<V> defaultValue)

我目前看到的目前解决方案:

  • defaultValue不一定是最终/有效的最终值
  • 看起来更简单&amp;不需要知道lambda语法

Disadventages:

  • 如果在使用getOrDefault时我们创建了新的对象,它将被创建并不断地传递给GC(使用供应商时,它根本不会被创建)。

我想知道是否还有任何使用和使用的不满情绪。有getOrDefault而不是getOrUseSupplier。你能告诉我,如果在java库中的任何地方,有这样的方法:

static <V> V getOrUseSupplier(Map<?, V> map, Object key, Supplier<V> supplier)

尝试从地图中获取值,如果它不存在,则从供应商处获取值。

5 个答案:

答案 0 :(得分:4)

我想这是因为没有炸毁Map界面。您可以使用Optional.orElseGet(Supplier)作为解决方法(如果您未在地图中保留空值):

Optional.ofNullable(map.get(key)).orElseGet(supplier)

答案 1 :(得分:2)

getOrUseSupplier()Map的最接近的等价物名为computeIfAbsent(),它允许使用键计算值,比仅使用{{1}时更灵活}。它还将计算值存储在Supplier中,与Map不同。这是因为它们具有不同的用例并且不是真正相关的。虽然getOrDefault通常用于返回&#34; safe&#34;非空的默认值(例如返回空字符串而不是空)表示 应该在地图中,getOrDefault表示某些必须地图,如果不是,则需要创建,否则程序的内部状态不正确。

以下示例忽略该密钥,只使用供应商的值。

computeIfAbsent()

答案 2 :(得分:1)

设计lambda时,在lambda-dev邮件列表中进行了关于是否包括与Supplier等效的getOrDefault的讨论。 Oracle的Brian Goetz将其从API中排除的原因有两个:避免不承担自身权重的特征的特征蠕变以及Lambda捕获的成本。

Question

有时构造默认对象的成本太高,并且依赖于 三元操作又是逃生舱口。一种 Map.getOrDefault(Object,Supplier)将解决此问题。

这曾经被考虑过吗?

Feature creep

蠕变警报!

getOrDefault几乎不承担任何责任,

Cost

顺便说一句,我们不愿让供应商满意的另一个原因是 是lambda的实际成本模型与预期成本模型之间的不匹配 捕获。

捕获无状态(非捕获)lambda基本上是免费的。

捕获有状态的,例如

   () -> state.toString()

目前具有与实例化内部类实例相当的成本 捕获局部变量。既然提供 供应商版本是成本,即使它可能会带来巨大的成本 减少,其交付的效果可能不如人们期望的那样。所以这 出于一个小考虑,不宜过于依赖此技巧。

我们正在研究可显着消除这些成本的VM工作, 但是首先我们得把8拿出来。

答案 3 :(得分:0)

使用getOrDefault()方法时,请记住,如果第二个参数(默认值)由于某种原因引发异常,则即使键存在于映射中,也会引发该异常,例如无论键是否存在,都会评估默认值。这是一个示例:

private static final Map<String, Integer> RANKS = new HashMap<>(Map.of(
        "A", 9,
        "B", 8,
        "C", 7,
        "D", 6
));

public static void main(final String[] args) {
    final int value1 = RANKS.getOrDefault("E", 5); // This will work
    final int value2 = RANKS.getOrDefault("A", Integer.valueOf("G")); // Exception!
}

如您所见,即使键A存在,它仍然会尝试评估默认值,在此示例中它本身会抛出一个NumberFormatException
如果您使用假设的getOrUseSupplier()方法(不幸的是,如@Kayaman所述,该方法尚不存在于Java中,您应该自己实现),您可以执行以下操作:

final int value = getOrUseSupplier(RANKS, "A", () -> Integer.parseInt("G")); // This will work

现在,仅当缺少键A时才会引发异常。

答案 4 :(得分:0)

Objects.requireNonNullElseGet()是实现所需的getOrUseSupplier(Object key, Supplier<V> defaultValueSupplier)行为的好方法。

Objects.requireNonNullElseGet(map.get(key), defaultValueSupplier));

优点(与其他解决方案相比):

  • 单线
  • 在地图上没有副作用
  • 不创建任何垃圾对象(例如Optional)
  • 核心Java(无需库)

需要Java 9。

我也很高兴在地图本身上也有这种方便的方法getOrUseSupplier()。 也许在即将发布的版本中...

UPD

我认为this mail from Brian Goetz是您原始问题的正式答案。 Java设计人员担心程序员会滥用这种方法捕获lambda。