map()
Transform the value inside an Optional without unwrapping it, so a missing value flows through the chain as empty instead of null.
public <U> Optional<U> map(Function<? super T, ? extends U> mapper)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$mapper | Function<? super T, ? extends U> | The function applied to the value, if present. Not called when the Optional is empty. |
Return value
Optional<U>An Optional holding the result of applying mapper to the value if a value is present and the result is non-null; otherwise an empty Optional.
Errors
NullPointerException if mapper is null.
map applies a function to the value inside an Optional and wraps the result in a new Optional. If the Optional is empty, the function is not called and you get an empty Optional back. That lets you write the steps for the present case and handle the missing case once, at the end.
String city = findCustomer(id)
.map(Customer::address)
.map(Address::city)
.orElse("unknown");A null result becomes empty
If the mapping function returns null, map returns Optional.empty() rather than throwing. This makes it a safe way to walk a chain of getters where any link may be null: above, a customer with no address simply produces "unknown". The same code with plain getters would need a null check at each step.
When to use flatMap instead
If the function already returns an Optional, such as another repository lookup, map wraps it again and you get Optional<Optional<Team>>. That is the signal to switch to flatMap, which takes a function returning Optional and does not add a second layer.
Mistakes people make
- Unwrapping too early:
if (opt.isPresent()) { dto = toDto(opt.get()); }.opt.map(this::toDto)does the same and keeps the result an Optional for the next step. - Doing side effects inside
map, such as saving or logging. UseifPresentfor actions;mapis for computing a new value. - Expecting
mapto catch exceptions. If the function throws, for exampleInteger.parseInton bad input, the exception propagates. Validate first, or write a helper that returnsOptional.empty()on failure and useflatMap.
Back-end angle
The typical use is converting an entity to a response DTO: repo.findById(id).map(UserResponse::from). Spring MVC goes one step further with ResponseEntity.of(optional), which returns 200 with the body or 404 when empty, so map is often the last thing a controller does before returning.
Example
When to use it
- Converting a loaded entity to a response DTO before returning it from a GET handler.
- Reading a nested property such as customer.address.city where any level may be null.
- Trimming, parsing and clamping an optional query parameter in one chain with a default at the end.
Frequently asked questions
What happens if the function in Optional.map returns null?
map returns an empty Optional. It never produces an Optional containing null, which is what makes it useful for chains of getters that may return null.
What is the difference between Optional.map and Optional.flatMap?
map wraps whatever the function returns in an Optional. flatMap expects the function to return an Optional itself and returns it as is, so you avoid Optional<Optional<T>>.
More examples
Entity to response DTO
map converts the entity to a DTO that leaves out the password hash, then into a response line. The missing user skips both steps and falls through to the 404.
record User(long id, String name, String passwordHash) {}
record UserResponse(long id, String name) {
static UserResponse from(User u) {
return new UserResponse(u.id(), u.name());
}
}
final Map<Long, User> users = Map.of(1L, new User(1L, "Ada", "$2a$10$fakehash"));
String getUser(long id) {
return Optional.ofNullable(users.get(id))
.map(UserResponse::from)
.map(r -> "200 " + r)
.orElse("404 user " + id + " not found");
}
void main() {
IO.println(getUser(1));
IO.println(getUser(2));
}
// Output:
// 200 UserResponse[id=1, name=Ada]
// 404 user 2 not foundA null from the mapper becomes empty
Map.get returns null for the missing header, and map turns that null into an empty Optional instead of Optional[null] or an exception.
void main() {
Map<String, String> headers = Map.of("Accept", "application/json");
Optional<Map<String, String>> request = Optional.of(headers);
IO.println(request.map(h -> h.get("Accept")));
IO.println(request.map(h -> h.get("X-Trace-Id")));
IO.println(request.map(h -> h.get("X-Trace-Id")).orElse("no trace id"));
}
// Output:
// Optional[application/json]
// Optional.empty
// no trace idParsing and clamping a query parameter
Each map step does one small job: trim, parse, clamp to the range 1 to 100 with Math.clamp (Java 21). A client asking for 500 rows gets 100, a negative limit becomes 1, and no parameter means the default of 20; non-numeric input would still throw from parseInt.
int limit(Map<String, String> query) {
return Optional.ofNullable(query.get("limit"))
.map(String::strip)
.map(Integer::parseInt)
.map(n -> Math.clamp(n, 1, 100))
.orElse(20);
}
void main() {
IO.println(limit(Map.of("limit", "500")));
IO.println(limit(Map.of("limit", " 15 ")));
IO.println(limit(Map.of("limit", "-5")));
IO.println(limit(Map.of()));
}
// Output:
// 100
// 15
// 1
// 20
Discussion