1. Overview
In this tutorial, we’ll see how to create null-safe streams from Java collections.
To start with, some familiarity with Java 8’s Method References, Lambda Expressions, Optional and Stream API is required to fully understand this material.
If you are unfamiliar with any of these topics, kindly take a look at our 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’re going to need for certain scenarios:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.2</version> </dependency>
The commons-collections4 library can be downloaded from Maven Central.
3. Creating Streams from Collections
The basic approach to 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 is 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 is 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 a wrong approach post-Java SE 8: There is a better way to model the absence and presence of a value.
It’s important to keep on mind that an empty Collection isn’t the same as a null Collection. While the first one is indicating that our query doesn’t have results or elements to show, the second one is suggesting that a kind of error just happened during the process.
4.2. Use emptyIfNull Method from CollectionUtils Library
We can opt to use 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 be arguably considered as 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 of 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 sample to:
Stream<String> collectionAsStream(Collection<String> collection) { return collection.stream().flatMap(s -> Stream.ofNullable(s)); }
5. Conclusion
In this article, we briefly revisited 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 weakness of using each strategy where relevant.
As usual, the full source code that accompanies the article is available over on GitHub.