在Java 8流中避免NoSuchElementException

3

这个问题是之前一个问题的后续: 使用流(Streams)将BigDecimals相加

该问题涉及使用Java 8 Stream和Lambda表达式将BigDecimal相加。在实现给出的答案后,我遇到了另一个问题:每当流为空时,Optional::get()方法会抛出NoSuchElementException异常。

考虑以下代码:

public static void main(String[] args){
    LinkedList<BigDecimal> values = new LinkedList<>();
//        values.add(BigDecimal.valueOf(.1));
//        values.add(BigDecimal.valueOf(1.1));
//        values.add(BigDecimal.valueOf(2.1));
//        values.add(BigDecimal.valueOf(.1));

    // Classical Java approach
    BigDecimal sum = BigDecimal.ZERO;
    for(BigDecimal value : values) {
        System.out.println(value);
        sum = sum.add(value);
    }
    System.out.println("Sum = " + sum);

    // Java 8 approach
    values.forEach((value) -> System.out.println(value));
    System.out.println("Sum = " + values.stream().reduce((x, y) -> x.add(y)).get());
}

原生Java代码在处理空集合时没有问题,但新的Java 8代码会出现错误。

有什么最优雅的方法可以避免NSEE(NullPointerException)?当然,我们可以这样做:

System.out.println("Sum = " + values == null || values.isEmpty() ? 0 : values.stream().reduce((x, y) -> x.add(y)).get());

但是有没有一种Java 8的方式来处理空集合呢?


反转顺序:values.stream().reduce((x, y) -> x.add(y)).ifPresent(s -> System.out.println("sum = " + s)); // 只有当和有值时才打印。 - user_3380739
2个回答

6
在这种情况下,您不应该使用可以返回Optional的reduce版本。正如之前提到的那样,您应该使用另一个版本,它在流为空时提供了一个身份元素,这就是身份元素存在的全部原因。
因此,您需要有:
System.out.println("Sum = " + values.stream().reduce(BigDecimal.ZERO, (x, y) -> x.add(y));

与旧版本不同。

在这种情况下,您不需要关心流是否为空,只需获得有效结果即可。


嗯,这就是身份值存在的原因吗?当流不为空时,身份值是否有任何作用?Stream类可用的众多选项可能会令人困惑。 - ryvantage
@ryvantage 身份值是 Java 7 代码中 for 循环之前的 BigDecimal sum = BigDecimal.ZERO; 的等同项。 - skiwi
2
是的,在这里使用带有身份值的两个参数reduce形式是合适的。身份值不仅在零元素流的情况下使用,而且在进行并行缩减时也用作部分结果的初始值。 - Stuart Marks

5

当我打出这个示例来问问题时,我找到了答案:

Stream::reduce() 返回一个 Optional,其中有一个方法:orElse()。所以,

System.out.println("Sum = " + values.stream().reduce((x, y) -> x.add(y)).get());

变成

System.out.println("Sum = " + values.stream().reduce((x, y) -> x.add(y)).orElse(BigDecimal.ZERO));

所以我决定发布一个问答。

Lambdas很棒。Java加一分。


你看过另一个答案了吗?它使用了重载的 reduce() 方法,并将 BigDecimal.ZERO 作为第一个参数传递。该方法仅返回 BigDecimal,而不是 Optional - Rohit Jain
5
错误在于对一个不确定是否非空的 Optional 调用 get() 方法。相反,应该使用其中一个条件方法,如 orElse() 或 ifPresent(),或根据 Optional.isPresent() 编写条件代码。 - Brian Goetz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接