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) на любой поток.