When to use Optional.stream()
Learn to integrate Java Optional into a stream Pipeline
Java Optional
Optional class was introduced in JDK 8, as a means to reduce the occurrence of NullPointerException in Java. At the most basic level, you can think about a Java Optional class as an immutable (which cannot be changed) container for the actual object that needs to be returned by a method.
The big idea here is that if a method returns a value which can be null, then that method could instead return an Optional with the value contained inside it. In other words, the method will not return a null but an Optional object which has a valid value or might be empty.
Typically a programmer who is developing a Java library, may use Optional types in the method return signature to convey to the caller about the possibility of returning null value.
One way to create an Optional object is to use the ofNullable(..) method as shown below. Here variable optUser is an Optional object of parameterized type User which can contain a valid User object or empty.
Optional<User> optUser
= Optional.ofNullable(someUser);
Additionally, the Optional class has a number of methods to extract the value from it. Methods like isPresent() and get() can be used with Imperative style programming. However, particularly interesting are the methods to handle the Optional value in a functional and fluent style. Here are some examples.
ifPresent(..), map(), filter(), flatMap(), or(), orElse(), stream()
One method among them — Optional.stream() — seems a little bizarre at first glance. After all, an Optional object either has a value or does not have a value. What on earth is the point of creating a stream out of it ?
Let’s analyze that a bit more.
Optional.stream()
Java stream programming has been very popular among Java developers. It encourages a functional style of programming and makes the developers job easier in many use cases which deal with Collections.
Here’s a simple example of a Java stream pipeline, where a list of names who have valid addresses are extracted from a list of Person objects.
List<Person> people = getPeople();
List<String> names
= people.stream()
.filter(Person::isAddressNotNull)
.map(Person::name)
.toList();
Such manipulation of collections are extremely straightforward using Java streams and it usually involves using the map() and filter() methods of the Stream interface.
Just like we can create a stream out of the Collection, we can also create a stream out of an Optional object. That’s exactly what the Optional.stream() method is about. When the stream() method is called on a Java Optional, it will return an empty stream if the Optional is empty and it will return a stream of one element if the Optional has a value. Essentially, we can think about an Optional as a collection of 0 or 1 element — even though it does not actually implement the Collection interface.
You might be wondering when such a stream can be useful. Let me show you where Optional.stream() method can be used effectively.
Integrate Optional into Java stream
Consider a User class which has the following attributes — name and address. Furthermore, assume that the developer returns the Optional type for the address — since it can be null.
public class User {
private final String name;
private final String address;
User(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Optional<String> getAddress() {
return Optional.ofNullable(address);
}
}
We have a factory class called Users which has a method called Users.getAllUsers(..) which returns all users in the system.
public class UserFactory {
public static List<User> getAllUsers() {
// Fake users - not important
User user1 = new User("John Doe", "Junk St, MD");
User user2 = new User("Jane Doe", "Test str, VA");
User user3 = new User("Jack Doe", null);
return List.of(user1, user2, user3);
}
}
Let’s implement the use case to display all the user addresses if they are available (not null) — very simple. We could do this in a functional way as follows.
Users.getAllUsers()
.stream()
.map(User::getAddress)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
// Output of above code
Junk St, MD
Test str, VA
We use the map(..) method to get a “stream of Optional<String>” — remember that the getAddress(..) returns an Optional<String>. We use the filter(..) method to only look at addresses which are “present” and then again use the map(..) to extract the address (once we know it cannot be null)
This works !
However, a shorter way of doing the same thing is to use the Optional.stream() method along with the flatMap(..) method of Stream interface.
Here’s how to use the Optional.stream(..) method.
Users.getAllUsers()
.stream()
.map(User::getAddress)
.flatMap(Optional::stream)
.forEach(System.out::println);
// Output of above code
Junk St, MD
Test str, VA
In this case, you see that after using the map(..) method to get a stream of Optional addresses, we use the flatMap(..) method. When we pass the method reference Optional::stream to flatMap(..), flatMap will internally call it on the Optional<String> object available from the previous map(..) method — and this results in a “stream of string”.
Note that — we cannot call map(..) instead of flatMap(..) here because otherwise the output will be “stream of stream of String”. The call to flatMap(..) will essentially flatten the “stream of stream of String” to “stream of String” making life simpler for us.
You can also combine map(..) and flatMap(..) as below — instead of using Optional method references. You might find this easier to understand.
Users.getAllUsers()
.stream()
.flatMap(u -> u.getAddress().stream())
.forEach(System.out::println);
// Output of above code
Junk St, MD
Test str, VA
Note that the map(..) and flatMap(..) methods are called on the stream and not on the Optional object.
Hope this gives you a good idea of when Optional.steam() can be used !
You may also want to check out my two other Medium articles on Java Optional which explains the basics as well as the Functional and Fluent style of development using Optional.
If this post was helpful, please click the clap 👏 button below a few times to show your support. Thanks for reading !
I regularly upload videos to my YouTube Channel at https://www.youtube.com/@viraj_shetty
Also check out my discounted Udemy Courses at https://www.mudraservices.com/courses/
Summary
Many of the Java libraries return Optional types as part of their method calls. So, it is important that these Optional objects be integrated into the Java stream Pipeline with minimal effort.
Optional.stream() method makes this integration easy.