How to work with Optional in Stream?
Optional and Stream often work together. Main scenarios:
🟢 Junior Level
Optional and Stream often work together. Main scenarios:
Optional.empty() — a “container without a value.” Analogous to null, but safe: you cannot accidentally get NPE, you must explicitly call get/orElse.
1. Stream contains Optional:
// Before: Stream<Optional<User>>
// Needed: Stream<User> (without empties)
ids.stream()
.map(repo::findById) // returns Optional<User>
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
2. Return Optional from stream operations:
Optional<User> firstAdmin = users.stream()
.filter(User::isAdmin)
.findFirst(); // returns Optional
Important: Never call .get() without checking .isPresent().
🟡 Middle Level
Java 9+ style: Optional.stream()
An elegant way to get rid of empty Optionals:
List<User> activeUsers = ids.stream()
.map(repository::findById) // Stream<Optional<User>>
.flatMap(Optional::stream) // Empty Optionals disappear!
.toList();
Under the hood: Optional.stream() returns Stream.of(value) or Stream.empty().
Avoid .get() in lambdas
// BAD — a ticking time bomb
.map(o -> o.get())
// GOOD — safe
.flatMap(Optional::stream)
.map(o -> o.orElse(defaultValue))
orElse() — is evaluated ALWAYS, even if Optional is not empty. orElseGet() — is evaluated ONLY if Optional is empty. If the default value is expensive (DB query) — always use orElseGet!
Advanced methods (Java 9-11)
ifPresentOrElse(action, emptyAction):
optional.ifPresentOrElse(
user -> sendNotification(user),
() -> log.warn("User not found")
);
or(Supplier):
// Search chain: cache → DB → API
findByIdInCache(id)
.or(() -> findByIdInDb(id))
.or(() -> findByIdInApi(id));
When NOT to use Optional in Stream
- Simple null check — regular
if (x != null)is more readable - Inside a tight loop — creating Optional = allocation, can stress GC
- As a class field — Optional is not serializable, not for fields
🔴 Senior Level
Do not use Optional for Entity fields
Storing Optional in class fields (especially JPA) is an anti-pattern:
Optionalis not serializable- Increases memory consumption
- Use only as a method return value
Highload: Optional Overhead
Creating millions of Optional objects can be costly:
- Value Types (Project Valhalla): In the future,
Optionalwill become an “Inline Type” with no overhead - Today: For billions of primitives, use “magic values” (
-1,0) orOptionalInt
Edge Cases
NullPointerException: Optional.of(null) throws NPE immediately. Use Optional.ofNullable().
Parallel Streams: Optional in parallel streams is safe — Optional is immutable.
Diagnostics
Sonar Rule: “Optional should be used as a return type only”. Ignore only in exceptional cases.
Debugging: When debugging flatMap(Optional::stream), empty values simply disappear from the visualizer — this can be confusing.
🎯 Interview Cheat Sheet
Must know:
filter(Optional::isPresent).map(Optional::get)— old Java 8 style for unwrappingflatMap(Optional::stream)— Java 9+, elegant way to remove empty OptionalsorElse()is evaluated ALWAYS,orElseGet()— only if Optional is empty (for expensive defaults)Optional.of(null)throws NPE — useOptional.ofNullable()- Optional — only as a method return value, NOT as a class field
- Optional is not serializable and not for JPA Entity fields
ifPresentOrElse(action, emptyAction)andor(Supplier)— Java 9+ for advanced handling- In tight loops, creating millions of Optionals stresses GC — consider OptionalInt/Long
Frequent follow-up questions:
- How does orElse differ from orElseGet? — orElse always evaluates its argument; orElseGet — only on empty Optional. For expensive defaults (DB query) — always orElseGet.
- **How to turn Stream<Optional
> into Stream ?** — `.flatMap(Optional::stream)` (Java 9+) — empty Optionals will disappear. - Why should Optional not be used as a class field? — Not serializable, increases memory, complicates code — intended only for return values.
- Is Optional safe in parallelStream? — Yes, Optional is immutable, but object creation overhead remains.
Red flags (DO NOT say):
- “You can call .get() without checking” — incorrect, an empty Optional will throw NoSuchElementException
- “Optional is a replacement for null in class fields” — incorrect, this is an anti-pattern, Optional is only for return types
- “orElse and orElseGet are the same” — incorrect, orElse is evaluated always, orElseGet — lazily
- “Optional.of(null) will return an empty Optional” — incorrect, it throws NullPointerException
Related topics:
- [[26. What do findFirst() and findAny() operations do]]
- [[25. What are anyMatch(), allMatch(), noneMatch() operations]]
- [[22. When does Stream operation execution begin]]
- [[27. How to collect Stream into Map]]