为什么Stream.reduce(BinaryOperator)在结果为null时抛出NullPointer?

时间:2017-03-03 14:36:14

标签: java java-stream

我有一个我希望减少的枚举值流。如果流为空或包含不同的值,我想要null。如果它只包含单个值的(多个实例),我想要该值。

[]              null
[A, B, A]       null
[A]             A
[A, A, A]       A

我尝试使用reduce:

return <lots of filtering, mapping, and other stream stuff>
       .reduce((enum1, enum2) -> enum1 == enum2 ? enum1 : null)
       .orElse(null);

不幸的是,这不起作用,因为当结果为NullPointerException时,此reduce方法会抛出null。有谁知道为什么会这样?为什么null不是有效的结果?

现在,我解决了这个问题:

MyEnum[] array = <lots of filtering, mapping, and other stream stuff>
                 .distinct()
                 .toArray(MyEnum[]::new);
return array.length == 1 ? array[0] : null;

虽然这有效,但我对此并不满意&#34;绕行&#34;。我喜欢reduce,因为它似乎是合适的,并将所有内容放在一个流中。

任何人都可以考虑使用reduce的替代方法(理想情况下代码不是太多)吗?

3 个答案:

答案 0 :(得分:1)

通常,返回Optional的所有Stream方法都不允许null值,因为无法区分null结果和“无结果”(空流)。

你可以使用占位符值解决这个问题,不幸的是,这需要暂停类型安全性(因为枚举集之外没有类型兼容的值):

return <lots of filtering, mapping, and other stream stuff>
    .reduce((enum1, enum2) -> enum1 == enum2? enum1: "")
    .map(r -> r==""? null: (MyEnum)r)
    .orElse(null);
如果映射函数返回Optional.map

null将返回一个空的可选项,因此在该步骤之后,无法再区分空流和null结果{{1在这两种情况下都会返回orElse(null)

但也许数组绕道只觉得不满意,因为阵列不是中间结果的最佳选择? <怎么样

null

如果EnumSet<MyEnum> set = <lots of filtering, mapping, and other stream stuff> .collect(Collectors.toCollection(() -> EnumSet.noneOf(MyEnum.class))); return set.size()==1? set.iterator().next(): null; 类型的常量不超过64,EnumSet只是一个bitset,一个long值。这比数组要便宜得多,因为enum自然是不同的,所以不需要对流进行Set操作,这会在引擎盖下创建distinct()

答案 1 :(得分:0)

大多数流高阶函数不允许null作为参数或函数返回值。他们要阻止另一个billion-dollar mistake。这样的回复记录在案here

  

可选reduce(BinaryOperator累加器)

     

.....

     

抛出:       NullPointerException - 如果减少的结果为null

答案 2 :(得分:0)

真正的数学方法怎么样(难以维持,我同意)?

Arrays.stream(array).map(e -> e.ordinal() + 1).reduce(Integer::sum)
            .map(i -> (double) i / array.length == array[0].ordinal() + 1 ? array[0] : null)
            .orElse(null)