【问题标题】:System.out.println() of Stream#reduce() unexpectedly prints "Optional[]" around resultStream#reduce() 的 System.out.println() 意外在结果周围打印“Optional[]”
【发布时间】:2016-02-13 19:07:48
【问题描述】:

我开始学习 Java 8 的 Lambda 表达式,并编写了以下程序来获取列表中所有数字的总和:

import java.util.Arrays;
import java.util.List;

public class MainClass {

    public static void main(String[] args) {
        List<Integer> number = Arrays.asList(1, 2, 3, 4, 5);

        System.out.println(number.stream().reduce((c,e) -> {
            return c + e;
        }));
    }
}

我期待的输出是:

15

但我得到了:

可选[15]

Java 版本:1.8.0_45

  • 请解释一下Optional[]在输出中是什么意思?
  • 它在 Java 8 中有什么意义吗?

【问题讨论】:

标签: java lambda java-8 stdout


【解决方案1】:

Java Docs for Stream#reduce(),我们看到reduce 操作返回Optional&lt;T&gt;。如果存在值,Optional 仅包装一个值,否则为“空”。

Optional 上的重要操作包括 Optional#isPresent,它可以让您知道 Optional 中是否有东西,Optional#get,它返回包裹在 Optional 中的 T,如果在 Empty 上调用则抛出异常, 和 Optional#orElse 如果存在则返回包裹在 Optional 中的 T,或者如果在 Empty 上调用则返回提供的默认值。

对于您的情况,reduce() 返回Optional&lt;Integer&gt; 背后的基本原理是您尝试减少的列表可能为空。在这种情况下,应该返回的Integer 定义不明确。对于您的特定意图,0 是可以接受的(因为空列表中元素的总和为 0),因此您可以获得如下总和:

int sum = number.stream().reduce((c,e) -> c + e).orElse(0);

这样,即使列表为空,您仍然会得到一个定义列表总和的结果。

【讨论】:

    【解决方案2】:

    reduce(BinaryOperator&lt;T&gt; accumulator):

    返回描述缩减结果的Optional

    Optional:

    一个容器对象,它可能包含也可能不包含非空值。如果存在值,isPresent() 将返回 trueget() 将返回该值。

    reduce() 返回 Optional 的原因是因为流可能是空的,在这种情况下不会有任何东西可以减少,也就是没有值,并且会返回 Optional.empty()

    【讨论】:

      【解决方案3】:

      为了避免返回中的 Optional,您可以调用此其他方法 https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-T-java.util.function.BinaryOperator- 只需在添加 lambda 表达式之前添加标识:“0”。请注意,现在只有 0 作为 reduce 调用的第一个参数。

       System.out.println(number.stream().reduce(0,(c,e) -> {
                  return c + e;
              }));
      

      只返回

      15
      

      【讨论】:

        【解决方案4】:

        知道了,谢谢@Mshnik 和@TimoSta。根据 Optional 的源代码覆盖toString 方法

        @Override
        public String toString() {
            return value != null
                ? String.format("Optional[%s]", value)
                : "Optional.empty";
        }
        

        上面的代码在我的输出流中添加了Optional[]

        【讨论】:

          【解决方案5】:

          来自java.lang.Object.Optional

          一个容器对象,它可能包含也可能不包含非空值。如果存在值,isPresent() 将返回 true,而 get() 将返回该值。

          Optional 提供了两种不同的主要方法来检索其值。

          public T get() 只返回包含在Optional 中的值,但如果Optional 没有包装值,则抛出NoSuchElementException

          orElse(T other) 返回包含在 Optional 中的值,如果 Optional 没有包含值,则返回 other

          编辑(感谢@Brian Goetz): 通常,orElse() 是更好的选择,因为如果 Optional 包含空值,get() 将返回 NoSuchElementException。没错,在这种情况下,您总是会收到 Optional 中的值,但主要使用 orElse() 仍然是一种好习惯。

          所以,在你的情况下,你会改变

          System.out.println(number.stream().reduce((c,e) -> {
                  return c + e;
          }));
          

          到:

          System.out.println(number.stream().reduce((c,e) -> {
                  return c + e;
          }).orElse(0));
          

          这将返回15的期望值。

          正如 @Brian Goetz 在 cmets 中所说,如果你真的想让它简洁,你可以使用 Integer::sum 和方法参考:

          System.out.println(number.stream.reduce(Integer::sum).orElse(0))
          

          这相当于使用更长的 lambda。

          【讨论】:

          • 也可以使用 Integer::sum 代替 lambda。 (另外,如果您更喜欢 lambda,则不需要使用语句 lambda,您可以使用表达式 lambda,去掉大括号并“返回”。)
          猜你喜欢
          • 1970-01-01
          • 2019-10-24
          • 1970-01-01
          • 2021-04-24
          • 1970-01-01
          • 1970-01-01
          • 2014-11-05
          • 2013-03-31
          • 1970-01-01
          相关资源
          最近更新 更多