14

Most probably a duplicate, however I was not able to find any particular one.

Given

public static void main(String[] args) {
    System.out.println(
            Arrays.asList(null, null, 1)
                    .stream()
                    .filter(obj -> obj == null)
                    .findAny()
                    .isPresent()
    );
}

Expectation

Should at least work (i.e return false because findAny returns Optional).

Actual

NullPointerException is thrown

Question

Is it a bug or a feature?

Thanks for your opinion and explanation.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
tkachuko
  • 1,956
  • 1
  • 13
  • 20

4 Answers4

16

If you change your code to use anyMatch instead of filter(...).findAny().isPresent(), it works as expected:

boolean found = Arrays.asList(null, null, 1)
        .stream()
        .anyMatch(Objects::isNull);

System.out.println(found); // true

As to why your version fails with NPE, in the Stream.findAny docs it says:

Throws:

NullPointerException - if the element selected is null

So this is expected behavior.


EDIT: The NPE occurs because Optional.of is used to construct the value returned by findAny(). And Optional.of requires a non-null value, as per the docs:

Returns an Optional with the specified present non-null value.

Anyways, I suppose you'd like to know why Optional.of was used instead of Optional.ofNullable, when building the value returned by findAny()...

Well, I can only speculate, but I think that findAny() and findFirst() are meant to find values that match some criteria, i.e. a Person whose name starts with A. It can be the case that you want to know whether there's a null element in your stream. But in this case, you don't need to actually find such element, because you already know that if you find it, it will be, well... just null. So it's enough to only check if your stream contains null, and you can perfectly use anyMatch() to find if that's the case.

In other words, it wouldn't be of much use to find an element that is null, because you can't do anything with it (apart from knowing that it's null).


EDIT 2: As user @holi-java indicates in his comment below, if findAny() returned Optional.ofNullable(null), then there would be no way to know whether null was found or not. In this case, the result would be ambiguous, because Optional.ofNullable(null).equals(Optional.empty()) == true, i.e. it would result in confusion, since Optional.ofNullable(null).isPresent() == false, meaning there was no matching value found.

Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110
  • 1
    well, good job. It is another time difference to submit, ^_^. – holi-java Jun 01 '17 at 19:13
  • a `Optional` has 2 states: `absent` & `present`. if `findAny` using `Optional.ofNullable` then it has 3 states: `absent` & `null` & `present`. if we want to distinct between `absent` and `null`, then we must compare with `optional == Optional.empty()` , and then we back to checking an object whether is null as `object == null` and `Optional` loses it role for avoid checking `null`s, and can't chain a `Optional` with 3 states as further. – holi-java Jun 01 '17 at 20:25
  • @holi-java maybe it was me that misunderstood you... I'm thinking it as follows: if findAny used Optional.ofNullable to return the found value, and if that found value was null, then you wouldn't be able to know if the value was found or not, because an Optional that has a null value is an empty Optional. – fps Jun 01 '17 at 20:43
  • maybe my bad English confused you, ^_^. in all, an `Optional` has 3 state is hard to use. we'll make flow control like this: `if(optional == Optional.empty()){...}else{ optional.map(..).orElse(...) }`. and then it loses the benefits of its existence. – holi-java Jun 01 '17 at 20:46
  • on the other hand , `Optional` is an value object, if we comparing them with `==` is meaningless. just as we comparing with 2 `Integer`s by using `new Integer(1).equals(new Integer(1))` not using `new Integer(1) == new Integer(1)`. – holi-java Jun 01 '17 at 20:53
  • sir, [the documentation](http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) is said what I already said... in all, an `Optional` is an value object comparing between them by using `==` may have unpredictable results and should be avoided. and it has 2 states: `absent` & `present`. but `findAny` needs return an `Optional` with 3 states. that violates the contract between them. – holi-java Jun 01 '17 at 21:01
  • @holi-java maybe yes, maybe no, but why would someone want to find and return a null value in a stream? It's enough to know that there's a null value, you don't need the value. – fps Jun 01 '17 at 21:05
  • because `Optional` has 2 states in jdk-8. if we want to add a new state in it. we need using additional checking since we can't edit the source code. maybe jdk will introduce the 3rd state in future version. but I think it will not be added in jdk, because add a new state will result in hard to use it. – holi-java Jun 01 '17 at 21:09
  • 1
    @holi-java my point is that you don't need three states with findAny, you only need two: found and not_found. For the very special case when you want to know if your stream has a null element, use anyMatch – fps Jun 01 '17 at 21:12
  • it makes sense. but jdk provides another additional option to do this: `stream.filter(it-> it != null).findFirst()`, and I think another one is for checking program bugs by `Optional.of`. – holi-java Jun 01 '17 at 21:15
  • sir, I found [this question](https://stackoverflow.com/a/32466863/7607923) answered in a year ago @Holger is there too... and it says what I have already said it will result in an `Optional` represent 3 states if using `Optional#ofNullable`. but mine is concrete than his. – holi-java Jun 01 '17 at 21:34
  • @holi-java well, that question and the answers show some good points, but still, I don't think we should worry. `Optional` has the two states we need when we want to find a value in a stream. The only exception is when you want to find null, which I think is quite useless. After all, null is a datavalue. If you know it's there, you've found it. – fps Jun 01 '17 at 22:12
  • yes, sir. your thought is reasonable. however `findAny` asserts that it will return a `Optional` with 2 states. so it doesn't does that. – holi-java Jun 02 '17 at 06:34
  • 3
    @Federico Peralta Schaffner: well, in principle, the search condition could *include* `null` as possible result, instead of being a search for *exactly* `null`. However, this can be considered a rare corner case. I never had a need for this. Besides that, if I want to know whether a list contains `null`, I’d use `list.contains(null)` instead of using the stream API… – Holger Jun 02 '17 at 08:59
  • @Holger you are right, the filter condition could be a complex predicate, i.e. `s -> s == null ||s.startsWith("a")` and that would be a problem... – fps Jun 02 '17 at 12:03
14

This behavior is highlighted in the Javadoc for findAny() https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#findAny--

Returns:an Optional describing some element of this stream, or an empty Optional if the stream is empty

Throws:NullPointerException - if the element selected is null

Since you are filtering so the Stream only contains nulls, you are getting a NullPointerException as expected.

Community
  • 1
  • 1
GAdams
  • 266
  • 1
  • 5
3

Here's the documentation of findAny(), this is what it says:

Throws: NullPointerException -

if the element selected is null

So, you will always get an NPE if you try to invoke findAny() on a null object.

You can use anyMatch instead, e.g.:

Arrays.asList(null, null, 1).stream().anyMatch(e -> e == null));
Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
2

It is not a bug, it is a result of calling get() on an instance of Optional which throws NPE. The exact call that causes it is findAny(), which gives the following stack trace:

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)

Also the documentation for findAny() specifies that NPE can be thrown:

Throws:NullPointerException - if the element selected is null

You can achieve the expected result using anyMatch():

Arrays.asList(null, null, 1).stream().anyMatch(obj -> obj == null)

Why ofNullable() was not used in the implementation of findAny()?

Designers of the API did not want to assume whether null means the value is not present (absent value) or is present but equals null. Also, you can still use map(Optional::isNullable) on any stream.

syntagma
  • 23,346
  • 16
  • 78
  • 134