Handle Missing Values Using Optional in Java
Problem
An Optional is a container that either holds a value or is empty, which makes "there might be no result" visible in the method's return type.
Search a list of usernames for the first one starting with a given letter, and handle both the found and not-found cases without null checks.
Java Program
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class OptionalWithStreams {
public static void main(String[] args) {
List<String> users = Arrays.asList("arjun", "bella", "chen", "diya");
Optional<String> found = users.stream().filter(u -> u.startsWith("c")).findFirst();
Optional<String> missing = users.stream().filter(u -> u.startsWith("z")).findFirst();
System.out.println("Found: " + found.map(String::toUpperCase).orElse("nobody"));
System.out.println("Missing: " + missing.map(String::toUpperCase).orElse("nobody"));
System.out.println("isPresent: " + found.isPresent() + ", " + missing.isPresent());
found.ifPresent(u -> System.out.println("Sending welcome email to " + u)); // runs only if present
missing.ifPresent(u -> System.out.println("This line never prints"));
}
}Output
Core Logic
findFirst() returns an Optional, and its methods describe what to do in each case without ever touching null.
- 1
filter(...).findFirst()gives anOptional<String>that may be empty. - 2
map(String::toUpperCase)transforms the value only if it's present. - 3
orElse("nobody")supplies a fallback when it's empty. - 4
ifPresent(...)runs an action only when there's a value.
nobody and its ifPresent block never runs.Key Point: Calling get() on an empty Optional throws NoSuchElementException — prefer orElse, orElseThrow or ifPresent.
Why: The search stops at the first match, scanning at most n names.
Key Concepts
Approach 2: Chaining with flatMap
import java.util.Optional;
public class OptionalChaining {
static class Address {
private final String city;
Address(String city) { this.city = city; }
Optional<String> getCity() { return Optional.ofNullable(city); }
}
static class User {
private final Address address;
User(Address address) { this.address = address; }
Optional<Address> getAddress() { return Optional.ofNullable(address); }
}
static String cityOf(User user) {
return Optional.ofNullable(user)
.flatMap(User::getAddress) // flatMap: the getter already returns an Optional
.flatMap(Address::getCity)
.orElse("unknown");
}
public static void main(String[] args) {
System.out.println(cityOf(new User(new Address("Pune"))));
System.out.println(cityOf(new User(new Address(null))));
System.out.println(cityOf(new User(null)));
System.out.println(cityOf(null));
}
}
Output
Core Logic
When getters themselves return Optionals, flatMap chains them, replacing a ladder of nested null checks.
- 1
Optional.ofNullable(user)starts the chain even if the user is null. - 2
flatMap(User::getAddress)andflatMap(Address::getCity)step through each level. - 3If any level is empty, the rest is skipped and
orElse("unknown")applies.
unknown.Key Point: Use map when a step returns a plain value and flatMap when it already returns an Optional — otherwise you get Optional<Optional<T>>.
Why: Each call walks a fixed number of levels.
Key Concepts
Interview Questions
What's the difference between orElse and orElseGet?
orElse(value) always evaluates its argument, even when unused. orElseGet(supplier) computes the fallback only when the Optional is empty.
Should Optional be used for fields or method parameters?
Generally no. It's designed as a return type; for fields and parameters, plain values with clear null handling are preferred.