Вызов 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-

Это нормально, но я не совсем понимаю, почему я не получаю NullPointerException, когда этот код запускается со списком, содержащим только один элемент. Я ожидал, что prev будет нулевым в таком случае. Я попробовал отладку, но кажется, что она перешагивает все лямбда-выражения, когда есть только один элемент.

Ответ 1

Как говорит 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 имеет по крайней мере два элемента.

Ответ 2

Лучше:

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

Эта перегрузка reduce() возвращает Необязательный; вслепую распаковка его с помощью get является опасной и рискует выбросить NSEE. Распакуйте его одним из безопасных операторов, например orElse или orElseThrow.

Если есть вероятность, что в вашем потоке есть нули, сначала фильтруйте его с помощью .filter(t -> t != null).

Ответ 3

NPE будет сброшен, если операция уменьшения фактически приведет к значению null. Например, если вы попытаетесь сделать 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();