【问题标题】:Whats the most elegant way to add two numbers that are Optional<BigDecimal>添加两个 Optional<BigDecimal> 数字的最优雅方法是什么
【发布时间】:2017-01-22 17:25:32
【问题描述】:

我需要对包含可选选项的两个大小数执行加法操作:

Optional<BigDecimal> ordersTotal;
Optional<BigDecimal> newOrder;

我要实现ordersTotal += newOrder 请务必注意,如果两个值都为空,则结果同样应为空(即不为零)。

这是我想出的:

ordersTotal = ordersTotal.flatMap( b -> Optional.of(b.add(newOrder.orElse(BigDecimal.ZERO))));

但我想知道是否有更优雅的解决方案。

【问题讨论】:

  • 如果都是空的,结果应该是0还是空?
  • 如果他们都是空的结果也应该是空的 - 将更新问题。

标签: java functional-programming java-8 optional


【解决方案1】:

我认为在可选项上使用方法流或方法链的建议答案非常聪明,但也许聪明得让人难以理解。 OP 已将此建模为ordersTotal += newOrder,但如果两者均为空,则结果应为空而不是零。也许编写代码这样说是合理的:

if (!ordersTotal.isPresent() && !newOrder.isPresent()) {
    result = Optional.empty();
} else {
    result = Optional.of(ordersTotal.orElse(ZERO).add(newOrder.orElse(ZERO)));
}

虽然这不是最短的,但它清楚地表达了 OP 的要求。

现在我已将计算值分配给 result,但 OP 实际上想将其分配回 ordersTotal。如果我们知道两者都是空的,那么我们可以跳过将空分配给ordersTotal 的 then 子句。这样做,然后反转条件可以得到更简单的结果:

if (ordersTotal.isPresent() || newOrder.isPresent()) {
    ordersTotal = Optional.of(ordersTotal.orElse(ZERO).add(newOrder.orElse(ZERO)));
}

现在,这往往会掩盖双空的特殊情况,这可能不是一个好主意。另一方面,这表示“如果其中一个为非空,则添加值”,这可能对应用程序很有意义。

【讨论】:

【解决方案2】:

不确定您是否会认为它更优雅,但这里有另一种选择:

ordersTotal = Optional.of(ordersTotal.orElse(BigDecimal.ZERO).add(newOrder.orElse(BigDecimal.ZERO)));

另一个,基于@user140547's suggestion

ordersTotal = Stream.of(ordersTotal, newOrder)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .reduce(BigDecimal::add);

请注意,第一个版本会返回 Optional.of(BigDecimal.ZERO),即使两个选项都为空,而第二个版本会在这种情况下返回 Optional.empty()

【讨论】:

  • 谢谢,我将使用第二个代码 sn-p - 它是最简洁和最通用的,因为它可以添加任意数量的选项
  • 在Java 9中,你可以使用Stream.of(ordersTotal, newOrder) .flatMap(Optional::stream) .reduce(BigDecimal::add);...
【解决方案3】:

您可以使用一系列选项。然后你可以制作一个 bigdecimals 流,然后减少这些 bigdecimals,否则返回 0。

这样做的好处是,如果您想更改两个以上的选项,则无需更改代码。

(如果需要可以稍后添加代码,目前我无法使用计算机)

【讨论】:

  • 我不想返回零。我想要一个空的可选(除非一个大小数实际上为零)。但是,您的解决方案似乎更加冗长
  • @maxTrialfire:请看 shmosel 的解决方案。 Java 9 将有一个 Optional.stream() 方法可以减少它的冗长。无论如何,在我看来,优雅也意味着通用性,所以你不必在使用 3 或 5 个 Optionals 时重写所有内容。
  • 是的。此外,我意识到“代码优雅”可能是主观的
【解决方案4】:

注意你的解决方案

ordersTotal=ordersTotal.flatMap(b -> Optional.of(b.add(newOrder.orElse(BigDecimal.ZERO))));

将产生一个空的Optional,如果ordersTotal 是空的,即使newOrder 不是。

这可以通过将其更改为来解决

ordersTotal=ordersTotal
    .map(b -> Optional.of(b.add(newOrder.orElse(BigDecimal.ZERO))))
    .orElse(newOrder);

但我更喜欢

ordersTotal=ordersTotal
    .map(b -> newOrder.map(b::add).orElse(b))
    .map(Optional::of).orElse(newOrder);

【讨论】:

    【解决方案5】:

    我知道这是一个旧线程,但是这个怎么样?

    orderTotal = !newOrder.isPresent()? 
                 orderTotal : 
                 newOrder.flatMap(v -> Optional.of(v.add(orderTotal.orElse(BigDecimal.ZERO));
    

    我对这种方法的想法是这样的:

    • 在所有闪亮的 Optional 等背后,这里的基本逻辑仍然是
      orderTotal += newOrder

    • 在第一个newOrder存在之前orderTotal不存在,在代码中用一个空的Optional表示。

    • 如果一个newOrder还不存在(另一个空的Optional),则根本不需要任何操作,即不需要修改orderTotal
    • 如果有 newOrder,请调用其 flatMap(..),如 maxTrialfire 的原始帖子中所述。

    【讨论】:

      【解决方案6】:

      此处的 Optional 和 Stream 不能优雅地结合在一起。

      java 8 中最好的是:

      ordersTotaI = !ordersTotaI.isPresent() ? newOrder
               : !newOrder.isPresent() ? ordersTotaI
               : Optional.of(ordersTotaI.get().add(newOrder.get()));
      

      不过好消息是,Java 9 将为 Optional 添加一些不错(但也很丑)的功能。

      【讨论】:

      • 我认为使用 isPresent() 违背了使用 Optionals 的目的。我不妨摆脱 Optional 包装器并使用内部 BigDecimal 并执行 if (value == null) 或三元运算符,如您的代码 value == null ? :
      • 如果我没记错的话,@maxTrialfire java 9 添加了.stream()。也许这有帮助。然而,已经可以很容易地获得 BigDecimal 流。一个空流应该产生一个 Optonal.empty,一个加到零的非空流。而我没有看到。你是对的。
      【解决方案7】:

      问题在于您的要求而不是您的解决方案。一个空的 Optional 不是零,而是一个缺失值。您基本上是在问 5 + NaN 等于 5。Optional 的 flatMap 会引导您走上幸福的道路:5 + Nan 是 Nan,而这正是 flatMap 所做的。

      【讨论】:

      • Stuart Marks 今天在 Java One 上展示了这一点,这是使用 isPresent 可能合法的少数情况之一。但我倾向于同意马里奥的观点——这个要求很奇怪。如果一个缺失的单个缺失参数被解释为零,那么同时缺失两个参数也应该产生一个零。这很容易通过两次调用orElse 来实现。但更有趣的问题是“如果两个论点都必须存在怎么办?”那么答案是first.flatMap(f -&gt; second.map(s -&gt; f.add(s)))
      【解决方案8】:

      考虑到您想重新分配给ordersTotal,您会注意到ordersTotal 仅在newOrder 存在时才会更改。

      因此,您可以从该检查开始,并将其写为:

      if (newOrder.isPresent()) {
          ordersTotal = newOrder.map(ordersTotal.orElse(ZERO)::add);
      }
      

      (这可以被认为是Stuart Marks' second solution的简化。这也是由于ordersTotal不是有效的最终方法而无法将方法引用转换回lambda的情况)

      如果你从这个检查开始,还有另一种可能的“聪明”方法:

      if (newOrder.isPresent()) {
          ordersTotal = ordersTotal.map(o -> newOrder.map(o::add)).orElse(newOrder);
      }
      

      中间map() 返回一个Optional&lt;Optional&lt;BigDecimal&gt;&gt;,其内部Optional 不能为空。由于可读性差,我不会认为它是一个好的解决方案,因此我会推荐第一个选项或 Stuart 的解决方案之一。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多