flatMap & Mapping
flatMap & Mapping
You already know that map() transforms each element of a stream into something else — one input, one output. But what happens when a single element naturally expands into many elements? That is exactly the problem flatMap() solves. Understanding when and why to reach for flatMap instead of map is one of the most important skills in working with the Streams API.
The Problem: Nested Structures
Imagine you have a list of sentences and you want a stream of every individual word. With a plain map() you get a Stream<String[]> — a stream of arrays — not a flat stream of strings:
Every sentence got turned into an array. You now have a stream of arrays, and iterating it hands you arrays, not individual words. flatMap() fixes this by first mapping each element to a stream, then flattening all those sub-streams into one continuous stream.
map is one-to-one. flatMap is one-to-many — each source element produces a stream of results, and those streams are all merged into the single output stream.
flatMap in Action
The lambda passed to flatMap must return a Stream. Here Arrays.stream(s.split(" ")) produces a small stream for each sentence. The API merges all those streams into one before any downstream operations see it.
Flattening a List of Lists
A classic use case is a nested collection — for example, each department holds a list of employees:
With map you would have gotten a Stream<List<String>>. With flatMap you get a single Stream<String> — every employee in every department, ready for further operations like sorted() or distinct().
Combining flatMap with Other Operations
Because flatMap produces a regular stream, you can chain any downstream operation after it. A common pattern is to flatten, filter, and then collect:
Collection::stream as a method reference instead of writing list -> list.stream() when the element type is a known collection. It is more concise and equally readable.
flatMap vs map: Choosing Correctly
- If the mapper returns a single transformed value, use
map. - If the mapper returns a collection or stream of values that should be merged into the main stream, use
flatMap. - If you use
mapwhen you neededflatMap, your element type becomesStream<Stream<T>>orStream<List<T>>— a common compile-time clue you have the wrong one.
Numeric Variants: flatMapToInt, flatMapToLong, flatMapToDouble
Just like map has mapToInt to avoid boxing, flatMap has primitive-specialised variants. If your sub-stream produces int values, use flatMapToInt to stay unboxed:
Stream<Integer> when you have primitive arrays. Each int gets boxed to Integer, which adds unnecessary heap allocation. Prefer flatMapToInt and stay on IntStream for numeric work.
Real-World Example: Extracting Tags
Consider a blogging platform where each post has a list of tags. You want the top 5 most frequently used tags across all posts:
Summary
flatMap is the tool for flattening one-to-many relationships in a stream pipeline. The key rule: your mapper must return a Stream, and all those sub-streams are automatically merged into one. Whenever you find yourself mapping to a collection and then struggling to work with a stream of collections, reach for flatMap. For primitive work, flatMapToInt, flatMapToLong, and flatMapToDouble eliminate boxing overhead.