skip to content

map vs flatMap

map gives one output per input, flatMap turns each element into a stream and flattens the results, which is how you unpack nested collections. The map-versus-flatMap question appears in nearly every streams interview.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

4

What is the difference between Stream.map and Stream.flatMap in Java?

level: juniorimportance: must knowfreq 85%

answer

  1. map = one-to-one, same count
  2. flatMap = one-to-many + flatten
  3. Stream<Stream<X>> signals flatMap
  4. flatMap function returns a Stream, map's returns a value
  5. flatMap can drop to zero (map+filter)

basics

~20 s

map transforms each element into exactly one new element (one-to-one). flatMap turns each element into a stream of elements and merges them all into one flat stream (one-to-many), so you end up with more or fewer elements, not the same count.

solid answer

~40 s

map applies a function to every element and produces exactly one output per input, keeping the stream the same size: Stream<T> becomes Stream<R>. flatMap is for when each element expands into several (or zero) values: its function returns a Stream for each element, and flatMap concatenates all those sub-streams into a single flat stream. The classic use is flattening nested structures, like turning a List<List<String>> into a Stream<String>, or splitting each sentence into its words. A simple rule: if your mapping function would otherwise hand you a Stream<Stream<X>> (a stream of streams), use flatMap instead of map to collapse it to Stream<X>. So map preserves cardinality; flatMap changes it because it both maps and flattens in one step.

go deeper

for a junior

Knows map is one-to-one and flatMap flattens nested collections; can give the List<List<X>> example.

for a middle

Articulates the cardinality difference precisely, recognizes the Stream<Stream<X>> smell, and knows flatMap's function returns a Stream.

for a senior

Explains flatMap as map+flatten in one step, notes it can also act as a filter (zero-output), and contrasts with Optional.map/flatMap.

for a principal

Frames map/flatMap as the functor map vs monadic bind pattern across Stream/Optional/CompletableFuture and discusses cost/allocation tradeoffs and mapMulti.

## Background: what a Stream is A **Stream** in Java (`java.util.stream.Stream`) is a sequence of elements you process with a pipeline of operations, e.g. `list.stream().filter(...).map(...).collect(...)`. Operations are either **intermediate** (lazy, return another stream — like `map` and `flatMap`) or **terminal** (trigger execution — like `collect`, `forEach`, `count`). ## map — one-to-one transformation `map` takes a `Function<T,R>` and applies it to **each** element, producing **exactly one** output per input. The number of elements is unchanged (cardinality is preserved); only their type/value changes. ```java Stream.of("a","bb","ccc") .map(String::length) // Function<String,Integer> .forEach(System.out::println); // 1, 2, 3 (3 in -> 3 out) ``` Think of `map` as `Stream<T> -> Stream<R>`. ## The problem map can't solve Suppose each element should expand into **several** values. Example: a `List<List<Integer>>` and you want every inner integer, or each sentence split into words. If you call `map`, the mapping function itself returns a collection or a stream, so you get a **stream of streams** (or a stream of lists): `Stream<Stream<Integer>>` or `Stream<List<Integer>>`. That nesting is awkward — you can't directly `sum()` or iterate the integers. ```java List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4)); nested.stream() .map(List::stream) // Stream<Stream<Integer>> -- nested! not what we want ... ``` ## flatMap — one-to-many + flatten `flatMap` takes a `Function<T, Stream<R>>`: for each element it returns a **whole stream** of zero, one, or many results. `flatMap` then **concatenates** (flattens) all those sub-streams into a single, flat `Stream<R>`. ```java List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4)); int sum = nested.stream() .flatMap(List::stream) // Stream<Integer>: 1,2,3,4 -- flat! .mapToInt(Integer::intValue) .sum(); // 10 ``` Because an element can map to **zero** items, `flatMap` can also shrink the stream (it acts as a combined map + filter + concat). ## The mental rule - Mapping function returns **one value** -> use `map` (cardinality preserved). - Mapping function returns **a stream/collection of values** (and you want them flattened) -> use `flatMap` (cardinality changes; nesting removed). - If you ever find yourself with `Stream<Stream<X>>` or `Stream<List<X>>` and wish it were `Stream<X>`, that's the `flatMap` signal. ## Related variants - Primitive forms exist: `mapToInt`, `flatMapToInt`, etc. - `Optional` also has `map`/`flatMap` with the same idea (`flatMap` avoids `Optional<Optional<X>>`). - Since Java 16 there is `mapMulti`, an imperative alternative that pushes results into a consumer rather than creating a stream per element (covered in a separate question).

  • What type does the function passed to flatMap return?
    A Stream (or in primitive forms an IntStream/LongStream/DoubleStream) of the result type — flatMap then concatenates all those streams into one.
  • Can flatMap reduce the number of elements in a stream?
    Yes. An element may map to an empty stream, contributing zero output elements, so flatMap can act like a combined map and filter.

saying these in an interview costs you the question

  • Saying flatMap and map are interchangeable
  • Claiming flatMap keeps the same number of elements
  • Thinking flatMap's function returns a single value rather than a stream
  • Confusing flatten with sorting or distinct

context

open as a page

How would you use flatMap to flatten a nested collection and split text into words, and why can't map do it?

level: middleimportance: should knowfreq 70%

basics

~20 s

Use flatMap with a function that turns each element into a stream — e.g. List::stream to flatten a List<List<X>>, or splitting each line into a stream of words. map can't because it would give one nested stream per element (a stream of streams) instead of merging them.

open as a page

What happens to the inner streams that flatMap produces, and why does that matter for resources like Files.lines?

level: seniorimportance: should knowfreq 45%

basics

~20 s

flatMap consumes each inner stream it creates and closes it when done. That matters because some streams hold resources — like Files.lines opening a file — so flatMap closing them after consumption helps avoid leaking file handles.

open as a page

What is Stream.mapMulti (Java 16), and when would you prefer it over flatMap?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

mapMulti is a Java 16 stream method that, like flatMap, lets each element produce zero or more outputs — but instead of returning a stream, you push results into a consumer. It avoids creating a small stream per element, so it can be faster and allocate less when each element yields few results.

open as a page