Почему findFirst () бросает исключение NullPointerException, если первый элемент, который он находит, равен null?
почему это бросает java.lang.NullPointerException
?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
первый элемент strings
и null
, что является вполне приемлемым значением. Кроме того, findFirst()
возвращает дополнительно, что имеет еще больше смысла для findFirst()
чтобы иметь возможность обрабатывать null
s.
изменить: обновлено orElse()
чтобы быть менее двусмысленным.
5 ответов:
причиной этого является использование
Optional<T>
В возврат. Необязательно не допускается содержатьnull
. По сути, он не предлагает никакого способа различения ситуаций "это не там "и" это там, но он установлен вnull
".вот почему документация явно запрещает ситуацию, когда
null
установлен вfindFirst()
:Броски:
NullPointerException
- если элемент выбранnull
как уже обсудили, разработчики API не предполагают, что разработчик хочет лечить
null
значения и отсутствующие значения таким же образом.если вы все еще хотите это сделать, вы можете сделать это явно, применив последовательность
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
в поток. Результат будет пустым необязательным в обоих случаях, если нет первого элемента или если первый элемент
null
. Так что в вашем случае, вы можете использоватьString firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(null);
получить
null
значение, если первый элемент отсутствует либоnull
.если вы хотите различать эти случаи, вы можете просто опустить
flatMap
действие:Optional<String> firstString = strings.stream() .map(Optional::ofNullable).findFirst().orElse(null); System.out.println(firstString==null? "no such element": firstString.orElse("first element is null"));
это не сильно отличается от вашего обновленного вопроса. Вы просто должны заменить
"no such element"
С"StringWhenListIsEmpty"
и"first element is null"
Сnull
. Но если вам не нравятся условные обозначения, вы можете достичь этого также, как:String firstString = strings.stream().skip(0) .map(Optional::ofNullable).findFirst() .orElseGet(()->Optional.of("StringWhenListIsEmpty")) .orElse(null);
теперь
firstString
будетnull
если элемент существует, ноnull
и это будет"StringWhenListIsEmpty"
когда элемент не существует.
следующий код заменяет
findFirst()
Сlimit(1)
и заменяетorElse()
Сreduce()
:String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
позволяет только 1 элемент для достиженияreduce
. ЭлементBinaryOperator
перешло кreduce
возвращает 1 элемент или другой"StringWhenListIsEmpty"
если никакие элементы не достигаютreduce
.красота этого решения заключается в том, что
Optional
не выделяется и тоBinaryOperator
лямбда не собирается ничего выделять.
можно использовать
java.util.Objects.nonNull
чтобы отфильтровать список, прежде чем найтичто-то вроде
list.stream().filter(Objects::nonNull).findFirst();
необязательным должен быть тип" значение". (читайте мелкий шрифт в документация:) JVM может даже заменить все
Optional<Foo>
СFoo
, удаление всех затрат на бокс и распаковку. Аnull
Foo означает пустойOptional<Foo>
.это возможный дизайн, чтобы разрешить необязательный с нулевым значением, без добавления логического флага-просто добавьте объект sentinel. (можно даже использовать
this
в качестве дозорного; видеть подобное.причина)решение, которое необязательно не может обернуть null не основан на стоимости выполнения. Это был очень спорный вопрос, и вам нужно копать списки рассылки. Решение не является убедительным для всех.
в любом случае, поскольку Optional не может обернуть нулевое значение, он толкает нас в угол в таких случаях, как
findFirst
. Они должны были рассудить, что нулевые значения очень редки (даже считалось, что поток должен содержать нулевые значения), поэтому удобнее выбрасывать исключение на нулевые значения, а не на пустые потоки.A обходной путь заключается в поле
null
, например,class Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();
(они говорят, что решение каждой проблемы ООП состоит в том, чтобы ввести другой тип :)