在只有一个元素的列表上调用 stream().reduce()

我是Java函数式编程的新手,并且想知道我应该如何编码以避免(例如)此操作中的NPE:

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

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

文档说该函数抛出“如果减少的结果是空的”:NullPointerException

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

这没关系,但我不太明白的是,当此代码在仅使用一个元素的列表运行时,为什么我没有得到一个。在这种情况下,我预计为空。我尝试调试,但是当只有一个元素时,它似乎只是单步执行整个lambda表达式。NullPointerExceptionprev


答案 1

正如 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 具有单个元素,则循环只有一次迭代,并且将返回在该迭代中找到的单个元素。

仅当 具有至少两个元素时,才会应用 。BinaryOperatorStream


答案 2

更好:

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

此 重载 返回一个 Optional;盲目地打开包装是危险的,并且有可能抛出NSEE。使用其中一个安全操作员(如 或 )将其打开包装。reduce()getorElseorElseThrow

如果流中可能存在空值,请先使用 过滤。.filter(t -> t != null)