仅使用一个元素在列表上调用stream()。reduce()

时间:2014-11-25 10:18:11

标签: java collections null functional-programming java-8

我是java中函数式编程的新手,并且想知道如何在(例如)此操作中编写代码来避免NPE:

myList.stream()
      .reduce((prev, curr) -> prev.getTimestamp().isAfter(curr.getTimestamp()) ? prev : curr);
      .get().getTimestamp();

我的目的是找到列表中最新对象的时间戳。关于如何更好地收集最后一个元素的建议非常受欢迎,但我这里的主要问题实际上是为什么这样做。

文档说该函数抛出NullPointerException"如果减少的结果为null":

http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-java.util.function.BinaryOperator-

没关系,但是我不太明白为什么当这个代码运行时只有一个元素的列表时我没有得到NullPointerException。在这种情况下,我希望prev为空。我试过调试,但是当只有一个元素时,它似乎只是跨过了整个lambda表达式。

3 个答案:

答案 0 :(得分:21)

正如reduce所述的JavaDoc所说,reduce相当于:

 boolean foundAny = false;
 T result = null;
 for (T element : this stream) {
     if (!foundAny) {
         foundAny = true;
         result = element;
     }
     else
         result = accumulator.apply(result, element);
 }
 return foundAny ? Optional.of(result) : Optional.empty();

因此,如果Stream只有一个元素,那么循环只有一次迭代,并返回在该迭代中找到的单个元素。

BinaryOperator仅在Stream至少包含两个元素时才会应用。

答案 1 :(得分:6)

更好:

  Optional<Thingie> max = 
      myList.stream()
            .reduce(BinaryOperator.maxBy(comparing(Thingie::getTimeStamp));

reduce()的这个重载返回一个Optional;盲目地用get打开它是危险的,并且冒着投掷NSEE的风险。请使用orElseorElseThrow等安全操作符打开包装。

如果您的信息流中有空值,请先使用.filter(t -> t != null)对其进行过滤。

答案 2 :(得分:2)

如果reduce操作实际导致null值,则会抛出NPE。例如,如果您尝试stream.reduce( (a,b) -> null )

另一方面,要找到最新的时间戳,您可以这样做:

myList.stream()
    .map(t -> t.getTimeStamp())
    .max(Comparator.naturalOrder()
    .get();  // will throw an exception if stream is empty

或者,使用静态导入并假设您的类名为Thing:

myList.stream().map(Thing::getTimeStamp).max(naturalOrder()).get();