How should we manage jdk8 stream for null values

2019-01-16 10:44发布

Hello fellow Java developers,

I know the subject may be a bit in advance as the JDK8 is not yet released (and not for now anyway..) but I was reading some articles about the Lambda expressions and particularly the part related to the new collection API known as Stream.

Here is the example as given in the Java Magazine article (it is an otter population algorithm..):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

My question is what happen if in the middle of the Set internal iteration, one of the otter is null?

I would expect a NullPointerException to be thrown but maybe am I still stuck in the previous development paradigm (non-functional), can someone enlighten me as how this should be handled?

If this really throw a NullPointerException, I find the feature quite dangerous and will have to be used only as below:

  • Developer to ensure there is no null value (maybe using a previous .filter(o -> o != null))
  • Developer to ensure the application is never generating null otter or a special NullOtter object to deal with.

What is the best option, or any other option?

Thanks!

5条回答
淡お忘
2楼-- · 2019-01-16 11:17

If you just want to filter null values out of a stream, you can simply use a method reference to java.util.Objects.nonNull(Object). From its documentation:

This method exists to be used as a Predicate, filter(Objects::nonNull)

For example:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

This will print:

Foo
Bar
查看更多
Juvenile、少年°
3楼-- · 2019-01-16 11:20

Current thinking seems to be to "tolerate" nulls, that is, to allow them in general, although some operations are less tolerant and may end up throwing NPE. See the discussion of nulls on the Lambda Libraries expert group mailing list, specifically this message. Consensus around option #3 subsequently emerged (with a notable objection from Doug Lea). So yes, the OP's concern about pipelines blowing up with NPE is valid.

It's not for nothing that Tony Hoare referred to nulls as the "Billion Dollar Mistake." Dealing with nulls is a real pain. Even with classic collections (without considering lambdas or streams) nulls are problematic. As fge mentioned in a comment, some collections allow nulls and others do not. With collections that allow nulls, this introduces ambiguities into the API. For example, with Map.get(), a null return indicates either that the key is present and its value is null, or that the key is absent. One has to do extra work to disambiguate these cases.

The usual use for null is to denote the absence of a value. The approach for dealing with this proposed for Java SE 8 is to introduce a new java.util.Optional type, which encapsulates the presence/absence of a value, along with behaviors of supplying a default value, or throwing an exception, or calling a function, etc. if the value is absent. Optional is used only by new APIs, though, everything else in the system still has to put up with the possibility of nulls.

My advice is to avoid actual null references to the greatest extent possible. It's hard to see from the example given how there could be a "null" Otter. But if one were necessary, the OP's suggestions of filtering out null values, or mapping them to a sentinel object (the Null Object Pattern) are fine approaches.

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-01-16 11:24

An example how to avoid null e.g. use filter before groupingBy

Filter out the null instances before groupingBy.

Here is an example

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
查看更多
Anthone
5楼-- · 2019-01-16 11:26

Although the answers are 100% correct, a small suggestion to improve null case handling of the list itself with Optional:

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

The part Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList) will allow you to handle nicely the case when listOfStuff is null and return an emptyList instead of failing with NullPointerException.

查看更多
beautiful°
6楼-- · 2019-01-16 11:34

Stuart's answer provides a great explanation, but I'd like to provide another example.

I ran into this issue when attempting to perform a reduce on a Stream containing null values (actually it was LongStream.average(), which is a type of reduction). Since average() returns OptionalDouble, I assumed the Stream could contain nulls but instead a NullPointerException was thrown. This is due to Stuart's explanation of null v. empty.

So, as the OP suggests, I added a filter like so:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

Or as tangens pointed out below, use the predicate provided by the Java API:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

From the mailing list discussion Stuart linked: Brian Goetz on nulls in Streams

查看更多
登录 后发表回答