Reputation: 14963
What are idiomatic ways of performing a null-check before generating a stream?
I have a method that receives a List
that might be null
. So I can't just call stream()
on the value that is passed. Is there some static helper in that would give me an empty stream if a value is null?
Upvotes: 51
Views: 65594
Reputation: 28988
Here's an option of generation a Stream from nullable source using Java 16 Stream.mapMulti()
.
This operation expects an argument of type BiConsumer
, i.e. a consumer, which in turn takes two arguments: a stream element and a consumer of the resulting type. Each value offered to the consumer becomes a new stream element, replacing the initial element.
public <T> Stream<T> getStream(Collection<? extends T> c) {
return Stream.ofNullable(c).mapMulti(Iterable::forEach);
}
Where the method reference Iterable::forEach
representing the BiConsumer
is an equivalent of the following lambda expression (explicit types provided for descriptiveness):
(Collection<? extends T> coll, Consumer<T> consumer) -> coll.forEach(consumer)
As well as flatMap()
operation, mapMulti()
is meant to perform one-to-many transformations replacing the consumed original stream element with 0+
(zero or more) elements.
Note that mapMulti()
would perform better than flatMap()
if you need to flatten a significant number of lists which contain only a few elements or can be empty. Here's a quote from the API Note:
This method is preferable to
flatMap
in the following circumstances:
- When replacing each stream element with a small (possibly zero) number of elements. Using this method avoids the overhead of creating a new
Stream
instance for every group of result elements, as required byflatMap
. ...
1. Lists (as well as other Collections and arrays) are containers of data, i.e. when you need something from a list you're interested the actual values are stored inside. A nullable container of data forces you to be right defensive unrelated to your business-logic and you or colleague might forget about that (which creates a problem which might surface at any moment).
Regardless of the source of nullable Collections, if you have a possibility to change the code you're working with, you can eliminate the actual problem, instead of hiding it using Stream.ofNullable()
or abusing Optional
.
These are possible sources of nullable Collections and treatments for them:
2. If you need a quick way to make the code working, but you want to be aware if the incoming list null
, then add the conditional statement to reflect the event in the logs:
if (list != null) logger.log( ... );
3. Lastly, as @Stuart Marks has pointed out in his answer to this question Optional
wasn't designed for performing/hiding null-checks. And here's a few more references regarding this topic:
Upvotes: 1
Reputation: 12024
Java 8:
Optional.ofNullable(list)
.orElseGet(Collections::emptyList)
.stream()
Java 9:
Stream.ofNullable(collection)
.flatMap(Collection::stream)
Apache Commons Collections 4:
import org.apache.commons.collections4.CollectionUtils;
CollectionUtils.emptyIfNull(collection)
.stream()
Upvotes: 14
Reputation: 7579
I agree with Stuart Marks that list == null ? Stream.empty() : list.stream()
is the right way to do this (see his answer), or at least the right way to do this pre-Java 9 (see edit below), but I'll leave this answer up to demonstrate usage of the Optional API.
<T> Stream<T> getStream(List<T> list) {
return Optional.ofNullable(list).map(List::stream).orElseGet(Stream::empty);
}
Edit: Java 9 added the static factory method Stream.<T>ofNullable(T)
, which returns the empty stream given a null
argument, otherwise a stream with the argument as its only element. If the argument is a collection, we can then flatMap
to turn it into a stream.
<T> Stream<T> fromNullableCollection(Collection<? extends T> collection) {
return Stream.ofNullable(collection).flatMap(Collection::stream);
}
This doesn't misuse the Optional API as discussed by Stuart Marks, and in contrast to the ternary operator solution, there's no opportunity for a null pointer exception (like if you weren't paying attention and screwed up the order of the operands). It also works with an upper-bounded wildcard without needing SuppressWarnings("unchecked")
thanks to the signature of flatMap
, so you can get a Stream<T>
from a collection of elements of any subtype of T
.
Upvotes: 84
Reputation: 51
Personally I consider null deprecated and use Optional wherever possible despite the (tiny) performance overhead. So I use the interface from Stuart Marks with an implementation based on gdejohn, i.e.
@SuppressWarnings("unchecked")
static <T> Stream<T> nullableCollectionToStream(Collection<? extends T> coll)
{
return (Stream<T>) Optional.ofNullable(coll)
.map(Collection::stream)
.orElseGet(Stream::empty);
}
Upvotes: 0
Reputation: 14550
apache commons-collections4:
CollectionUtils.emptyIfNull(list).stream()
Upvotes: 13
Reputation: 132410
In the other answers, the Optional
instance is created and used strictly within the same statement. The Optional
class is primarily useful for communicating with the caller about presence or absence of a return value, fused with the actual value if present. Using it wholly within a single method seems unnecessary.
Let me propose the following more prosaic technique:
static <T> Stream<T> nullableListToStream(List<T> list) {
return list == null ? Stream.empty() : list.stream();
}
I guess the ternary operator is somewhat déclassé these days, but I think this is the simplest and most efficient of the solutions.
If I were writing this for real (that is, for a real library, not just sample code on Stack Overflow) I'd put in wildcards so that that the stream return type can vary from the List type. Oh, and it can be a Collection, since that's where the stream()
method is defined:
@SuppressWarnings("unchecked")
static <T> Stream<T> nullableCollectionToStream(Collection<? extends T> coll) {
return coll == null ? Stream.empty() : (Stream<T>)coll.stream();
}
(The warning suppression is necessary because of the cast from Stream<? extends T>
to Stream<T>
which is safe, but the compiler doesn't know that.)
Upvotes: 18
Reputation: 14963
The best thing I can think of would be to use an Optional
with the orElseGet
method.
return Optional.ofNullable(userList)
.orElseGet(Collections::emptyList)
.stream()
.map(user -> user.getName())
.collect(toList());
Updated with @Misha's suggest to use Collections::emptyList
over ArrayList::new
Upvotes: 31