How to Convert Optional Values into Streams

Author      Ter-Petrosyan Hakob

In Java, an Optional<T> can either contain a value or be empty. Sometimes, we want to turn an Optional into a Stream so we can work with it more easily. The stream() method does exactly that: it creates a stream with one element if the Optional has a value, or no elements if it is empty.

But why would we want to do this? Let’s see a practical example.

Using Optional with Lists of IDs

Suppose you have a list of customer IDs and a method that finds a customer by ID:

Optional<Customer> findCustomer(String id)

You want to create a stream of valid customers, skipping IDs that do not exist in your database. You could first filter and then get the value like this:

Stream<String> ids = ...;
Stream<Customer> customers = ids.map(CustomerService::findCustomer)
                                .filter(Optional::isPresent)
                                .map(Optional::get);

This works, but it is a bit clunky because it uses isPresent() and get(). A cleaner way is:

Stream<Customer> customers = ids.map(CustomerService::findCustomer)
                                .flatMap(Optional::stream);

Here’s what happens:

This is shorter, safer, and easier to read.

Handling Methods That Return Null

Not all methods return Optional. Some older methods return null when a value is missing. For example:

Customer CustomerService.oldFind(String id) // returns null if no customer

You could filter out null values like this:

Stream<Customer> customers = ids.map(CustomerService::oldFind)
                                .filter(Objects::nonNull);

Or, using Stream.ofNullable() with flatMap, you can write it more elegantly:

Stream<Customer> customers = ids.flatMap(id -> Stream.ofNullable(CustomerService.oldFind(id)));

Or even:

Stream<Customer> customers = ids.map(CustomerService::oldFind)
                                .flatMap(Stream::ofNullable);

Stream.ofNullable(obj) creates:

This method avoids manual null checks and makes the code cleaner.

A Fresh Example

Imagine you have a list of book ISBNs and a method that finds books in your system:

Optional<Book> findBook(String isbn)

You can get a stream of available books like this:

List<String> bookIds = List.of("B101", "B102", "B103");
Stream<Book> books = bookIds.stream()
                            .map(BookService::findBook)
                            .flatMap(Optional::stream);

If B102 does not exist, it will be skipped. You will get a stream with only B101 and B103.

Why This Is Useful

This approach is very helpful when processing lists of IDs, products, users, or any items that may be missing.