1. Overview
In this tutorial, we’ll learn how to create null-safe streams from Java collections.
To fully understand this material, some familiarity with Java 8’s Method References, Lambda Expressions, Optional and Stream API is required.
If you’re unfamiliar with any of these topics, you can take a look at these previous articles first: New Features in Java 8, Guide To Java 8 Optional and Introduction to Java 8 Streams.
2. Maven Dependency
Before we begin, there’s one Maven dependency that we’ll need for certain scenarios:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.5.0-M2</version>
</dependency>
The commons-collections4 library can be downloaded from Maven Central.
3. Creating Streams From Collections
The basic approach for creating a Stream from any type of Collection is to call the stream() or parallelStream() methods on the collection, depending on the type of stream that’s required:
Collection<String> collection = Arrays.asList("a", "b", "c");
Stream<String> streamOfCollection = collection.stream();
Our collection will most likely have an external source at some point. We’ll probably end up with a method similar to the one below when creating streams from collections:
public Stream<String> collectionAsStream(Collection<String> collection) {
return collection.stream();
}
This can cause some problems. When the provided collection points to a null reference, the code will throw a NullPointerException at runtime.
The following section addresses how we can protect against this.
4. Making Created Collection Streams Null-Safe
4.1. Add Checks to Prevent Null Dereferences
To prevent unintended null pointer exceptions, we can opt to add checks to prevent null references when creating streams from collections:
Stream<String> collectionAsStream(Collection<String> collection) {
return collection == null
? Stream.empty()
: collection.stream();
}
This method, however, has a couple of issues.
First, the null check gets in the way of the business logic decreasing the overall readability of the program.
Second, the use of null to represent the absence of a value is considered the wrong approach post-Java SE 8; there’s a better way to model the absence and presence of a value.
It’s important to keep in mind that an empty Collection isn’t the same as a null Collection. While the first one indicates that our query doesn’t have results or elements to show, the second one suggests that a kind of error just happened during the process.
4.2. Use emptyIfNull Method From CollectionUtils Library
We can opt to use the Apache Commons’ CollectionUtils library to make sure our stream is null safe. This library provides an emptyIfNull method, which returns an immutable empty collection given a null collection as an argument, or the collection itself otherwise:
public Stream<String> collectionAsStream(Collection<String> collection) {
return emptyIfNull(collection).stream();
}
This is a very simple strategy to adopt; however, it depends on an external library. If a software development policy restricts the use of such a library, then this solution is rendered null and void.
4.3. Use Java 8’s Optional
Java SE 8’s Optional is a single-value container that either contains a value or doesn’t. Where a value is missing, the Optional container is said to be empty.
Using Optional can arguably be considered the best overall strategy to create a null-safe collection from a stream.
Let’s see how we can use it, followed by a quick discussion below:
public Stream<String> collectionToStream(Collection<String> collection) {
return Optional.ofNullable(collection)
.map(Collection::stream)
.orElseGet(Stream::empty);
}
- Optional.ofNullable(collection) creates an Optional object from the passed-in collection. An empty Optional object is created if the collection is null.
- map(Collection::stream) extracts the value contained in the Optional object as an argument to the map method (Collection.stream()).
- orElseGet(Stream::empty) returns the fallback value in the event that the Optional object is empty, i.e the passed-in collection is null.
As a result, we proactively protect our code against unintended null pointer exceptions.
4.4. Use Java 9’s Stream OfNullable
Examining our previous ternary example in section 4.1., and considering the possibility that some elements could be null instead of the Collection, we have at our disposal the ofNullable method in the Stream class.
We can transform the above example to:
Stream<String> collectionAsStream(Collection<String> collection) {
return collection.stream().flatMap(s -> Stream.ofNullable(s));
}
5. Conclusion
In this article, we briefly discussed how to create a stream from a given collection. We then proceeded to explore the three key strategies for making sure the created stream is null-safe when created from a collection.
Finally, we pointed out the disadvantages of using each strategy where relevant.
As usual, the full source code that accompanies the article is available over on GitHub.