What is the difference between map() and flatMap()?
The main difference is what the mapper function returns:
Junior Level
The main difference is what the mapper function returns:
| Criterion | map() |
flatMap() |
|---|---|---|
| When | 1->1 transformation | 1->N transformation + merging |
| Mapper return | R (one object) |
Stream<R> (stream of objects) |
| Correspondence | 1-to-1 | 1-to-many |
| Nesting | No | Collapses nested streams |
map(Function<T, R>):
- Function returns one object of type
R - 1-to-1 correspondence
List<String> words = List.of("hello", "world"); words.stream().map(String::length) // [5, 5]
flatMap(Function<T, Stream<R>>):
- Function returns a stream of objects of type
R - 1-to-many correspondence (0, 1 or many)
List<List<String>> nested = List.of(List.of("a", "b"), List.of("c")); nested.stream().flatMap(Collection::stream) // ["a", "b", "c"]
Simple rule: If you have a collection inside an element — flatMap, if simple transformation — map.
Middle Level
Signatures
// map — takes T, returns R
<R> Stream<R> map(Function<T, R> mapper)
// flatMap — takes T, returns Stream<R>
<R> Stream<R> flatMap(Function<T, Stream<R>> mapper)
When to choose map
- Data transformation (
User -> UserDTO) - Field extraction (
User -> User.getEmail()) - Type changes but structure is linear
When to choose flatMap
- Object contains a collection (
Order -> Stream<OrderItem>) - External sources returning streams
- Filtering + transformation simultaneously (returning
Stream.empty())
Optional: a special difference
Optional<User> user = ...;
// map will return Optional<Optional<String>> — nesting!
Optional<Optional<String>> bad = user.map(u -> u.getEmailAsync());
// flatMap will return Optional<String> — correct
Optional<String> good = user.flatMap(u -> u.getEmailAsync());
In Optional, flatMap “collapses” nesting.
When NOT to use either
- NOT flatMap if you just need to transform an element — use
map() - NOT map if an element turns into a collection and you want one flat result —
map()givesStream<List<T>>, but you needStream<T> - NOT either if you just need to filter —
filter()is more readable
Senior Level
Performance
Memory Overhead: flatMap is more expensive — a new Stream object is created for each element. With millions of elements this creates pressure on Young Gen.
Short-Circuiting: flatMap supports short-circuiting. If .findFirst() is placed after it, execution will stop after the first element from the first nested stream.
Parallelism: flatMap in parallel streams is less efficient — splitting becomes less predictable due to the variable number of output elements.
Null Handling
- In
map: returningnullcreates a stream with anullelement - In
flatMap: returningnullinstead of a stream ->NullPointerException
Infinite Streams
flatMap can combine infinite streams, but the terminal operation will not terminate (unless there is short-circuiting).
Diagnostics
- Type Checking: If you see
Stream<List<String>>— you most likely forgotflatMap - Testing: Always test cases where the mapper returns an empty stream and a stream with a single
nullelement
Interview Cheat Sheet
Must know:
map(Function<T, R>)— returns one object R, 1-to-1 correspondenceflatMap(Function<T, Stream<R>>)— returns a stream of objects, collapses nesting, 1-to-many- map for data transformation (User -> UserDTO), flatMap for extracting collections (Order -> Stream
) - In Optional: map can create
Optional<Optional<T>>, flatMap always returns a flatOptional<T> - flatMap is more expensive — creates a new Stream for each element
- Both support short-circuiting: findFirst() will stop execution after the first element
Common follow-up questions:
- When does map give Stream<List
>? — When the mapper extracts a collection from an element but you need Stream -> use flatMap - null in map vs flatMap? — map passes null further, flatMap throws NullPointerException
- When is flatMap slower? — With millions of elements due to Stream object allocation
- Can flatMap replace filter? — Yes, by returning Stream.empty() for filtered ones, but filter is more readable
Red flags (DO NOT say):
- “map and flatMap are interchangeable” — no, different signatures and semantics
- “flatMap is faster than map” — no, flatMap is always more expensive due to Stream object creation
- “map can return 0 elements” — no, map always returns 1 object (or null)
- “flatMap guarantees order in parallel streams” — no, splitting is less predictable
Related topics:
- [[4. What does map() operation do]]
- [[7. What does flatMap() operation do]]
- [[9. What are parallel streams]]
- [[2. What is the difference between intermediate and terminal operations]]