๐ŸŒŠ Streams API ยท Intermediate

flatMap in Java

Flattening nested structures into one stream.

๐Ÿงฉ The mysteryTwo lines of text, five words. map + split + count() says 2. You wanted 5. What's the missing ingredient?

Boxes inside boxes

If each element holds several values, map gives you a stream of containers โ€” one per input. **flatMap opens every container and pours its contents onto one flat belt.** map: one in โ†’ one out. flatMap: one in โ†’ zero or more out.

The rule: return a Stream

flatMap's function must return a Stream โ€” for lists, that's List::stream. flatMap then joins all those little streams into one. An empty inner list contributes nothing.

List<List<Integer>> nested = List.of(
    List.of(1, 2), List.of(), List.of(3));
List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .toList(); // [1, 2, 3]
๐Ÿ”ฎ Predict it

map vs flatMap

What does this print?

long a = Stream.of("hi", "yo")
    .map(s -> s.split(""))
    .count();
long b = Stream.of("hi", "yo")
    .flatMap(s -> Arrays.stream(s.split("")))
    .count();
System.out.println(a + " " + b);
  1. 4 4
  2. 2 4
  3. 2 2
Show the answer

2 4 โ€” map turns each word into one array, so there are still 2 elements. flatMap streams each array's letters and merges them into 4.

Returning a List instead of a Stream

โœ— Compile error
orders.stream()
    .flatMap(Order::items)  // a List!
    .toList();

items() returns a List, but flatMap requires a Stream. .flatMap(g -> g) on lists fails the same way.

โœ“ Compiles and flattens
orders.stream()
    .flatMap(o -> o.items().stream())
    .toList();

Turn each inner list into a stream and you get one Stream<Item>. (map(Order::items) would give a Stream<List<Item>>.)

mapMulti: the imperative cousin

Java 16 added **mapMulti: instead of returning a stream, you push** zero or more results into a consumer. Handy when building a little stream per element feels heavy.

List<Integer> r = Stream.of(1, 2, 3)
    .<Integer>mapMulti((n, out) -> {
        if (n != 2) out.accept(n * 10);
    })
    .toList(); // [10, 30]
๐Ÿ’ผ In the real world

Flattening is everywhere

APIs return nested data constantly: orders โ†’ line items, users โ†’ roles, files โ†’ lines โ†’ words. flatMap is how you get "all items across all orders" in one line โ€” and "use flatMap vs map" is a favourite interview question.

Key takeaways

  1. map: one element in โ†’ one element out
  2. flatMap: one element in โ†’ zero or more elements out
  3. The function must return a Stream, e.g. List::stream
  4. Java 16 added mapMulti as an imperative alternative

๐Ÿ’ก flatMap is like emptying several shopping bags onto one checkout belt.

๐Ÿคฏ Did you know?

flatMap isn't stream-specific jargon: Optional has a flatMap too, and the same idea powers flatMap in Scala, Kotlin, JavaScript and Rust.

Practice questions

What does this print?

List<List<Integer>> nested = List.of(
    List.of(1, 2), List.of(3), List.of());
List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .toList();
System.out.println(flat);
  1. [[1, 2], [3], []]
  2. [1, 2, 3, []]
  3. [1, 2, 3]
  4. [3]
Check your answer

[1, 2, 3]. Each inner list becomes its own stream and flatMap concatenates them. The empty list contributes nothing.

What does this print?

long n = Stream.of("a b", "c d e")
    .map(s -> s.split(" "))
    .count();
long m = Stream.of("a b", "c d e")
    .flatMap(s -> Arrays.stream(s.split(" ")))
    .count();
System.out.println(n + " " + m);
  1. 5 5
  2. 2 2
  3. 5 2
  4. 2 5
Check your answer

2 5. map turns each line into ONE array, so there are still 2 elements. flatMap streams each array's words and merges them into 5 elements.

Next: operations that must *remember* what they've seen โ€” and one of them can freeze your program on an infinite stream.