Java Streams: найти, если поток содержит null
скорее всего дубликат, однако я не смог найти какой-то конкретный.
дано
public static void main(String[] args) {
System.out.println(
Arrays.asList(null, null, 1)
.stream()
.filter(obj -> obj == null)
.findAny()
.isPresent()
);
}
ожидание
должен по крайней мере работать (i.e return false, потому что найти любое возвращает дополнительно).
фактический
NullPointerException
выдается
вопрос
это баг или фича?
Спасибо за Ваше мнение и объяснение.
4 ответов
Это поведение выделено в Javadoc для findAny() https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#findAny--
Returns: необязательное описание некоторого элемента этого потока или пустой необязательно, если поток пуст
Throws:NullPointerException - если выбранный элемент равен null
поскольку вы фильтруете, поэтому поток содержит только нули, вы получаете NullPointerException как и ожидалось.
если вы измените свой код для использования anyMatch
вместо filter(...).findAny().isPresent()
, он работает так, как ожидалось:
boolean found = Arrays.asList(null, null, 1)
.stream()
.anyMatch(Objects::isNull);
System.out.println(found); // true
, почему ваша версия не с НПЭ в Stream.findAny
docs он говорит:
Броски:
NullPointerException - if the element selected is null
Итак, это ожидаемое поведение.
EDIT:
NPE происходит потому, что Optional.of
используется для построения значения, возвращаемого findAny()
. И Optional.of
требует ненулевого значения, согласно документам:
возвращает необязательное значение с указанным настоящим ненулевым значением.
в любом случае, я полагаю, вы хотели бы знать, почему Optional.of
вместо Optional.ofNullable
, при построении значения, возвращаемого findAny()
...
Ну, я могу только предполагать, но я думаю, что findAny()
и findFirst()
предназначены для поиска значений, соответствующих некоторым критериям, т. е. Person
чье имя начинается с A
. Это может быть так, что вы хотите знаю есть ли null
элемент в свой поток. Но в этом случае, вам не нужно на самом деле найти такой элемент, потому что вы уже знаете, что если вы его найдете, то будет, ну... просто null
. Так что достаточно только Регистрация если ваш поток содержит null
, и вы можете отлично использовать anyMatch()
чтобы узнать, так ли это.
другими словами, было бы не очень полезно найти элемент null
, потому что вы ничего не можете с ним сделать (кроме того, что знаете, что это null
).
EDIT 2: как указывает пользователь @holi-java в своем комментарии ниже, если findAny()
вернулся Optional.ofNullable(null)
, тогда не было бы никакого способа узнать,null
встретился или нет. В этом случае результат будет неоднозначным, потому что Optional.ofNullable(null).equals(Optional.empty()) == true
, то есть это приведет к путанице, так как Optional.ofNullable(null).isPresent() == false
, что означает, что не было найдено соответствующего значения.
здесь документация findAny()
, вот что он говорит:
закидываем: NullPointerException -
если выбранный элемент равен null
таким образом, вы всегда получите NPE, если попытаетесь вызвать findAny()
на объект null.
можно использовать anyMatch
, а не, например:
Arrays.asList(null, null, 1).stream().anyMatch(e -> e == null));
это не ошибка, это результат вызова get()
на примере Optional
который бросает NPE. Точный вызов, который вызывает это findAny()
, который дает следующую трассировку стека:
Exception in thread "main" java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Optional.<init>(Optional.java:96)
at java.util.Optional.of(Optional.java:108)
at java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:193)
at java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:190)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:469)
at StreamTest.main(StreamTest.java:17)
и документация findAny()
указывает, что NPE может быть брошен:
Throws:NullPointerException - если выбранный элемент равен null
вы можете достичь ожидаемого результата, используя anyMatch()
:
Arrays.asList(null, null, 1).stream().anyMatch(obj -> obj == null)
почему ofNullable()
не использовался в реализации findAny()
?
разработчики API не хотели предполагать, что null
означает, что значение отсутствует (отсутствует значение) или присутствует, но равно null. Кроме того, вы все еще можете использовать map(Optional::isNullable)
на любой поток.