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