Question 29 · Section 8

How to work with Optional in Stream?

Optional and Stream often work together. Main scenarios:

Language versions: English Russian Ukrainian

🟢 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

  1. Simple null check — regular if (x != null) is more readable
  2. Inside a tight loop — creating Optional = allocation, can stress GC
  3. 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:

  • Optional is 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, Optional will become an “Inline Type” with no overhead
  • Today: For billions of primitives, use “magic values” (-1, 0) or OptionalInt

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 unwrapping
  • flatMap(Optional::stream) — Java 9+, elegant way to remove empty Optionals
  • orElse() is evaluated ALWAYS, orElseGet() — only if Optional is empty (for expensive defaults)
  • Optional.of(null) throws NPE — use Optional.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) and or(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]]