Definition
- **Flatten nested**: `List<List<T>>` to `Stream<T>` using `flatMap(Collection::stream)`.
- **Split values**: CSV line to tokens using `flatMap(line -> Arrays.stream(line.split(",")))`.
- **Optional extraction**: `flatMap(Optional::stream)` converts present Optionals to values and removes empties.
- **Merge sources**: `Stream.of(stream1, stream2).flatMap(s -> s)` merges multiple streams.
- **Multi-level**: chained flatMap calls drill through customer -> accounts -> transactions.flatMap is the envelope opener at National Bank of India. A bag of envelopes each containing multiple cheques: flatMap opens each and puts all cheques on one belt. A corporate cheque listing multiple employees: flatMap splits it into individual cheques. Some envelopes are empty: flatMap discards them. Three departments each with their own stack: flatMap merges all stacks into one.
Understanding the Concepts
What are the five use cases of flatMap?
Use case 1: Flatten nested collections
package com.bank.service;
import java.util.Collection;
import java.util.List;
public class FlattenDemo {
record Branch(String name, List<String> accountIds) {}
public static void main(String[] args) {
List<Branch> branches = List.of(
new Branch("Mumbai", List.of("SAV2001", "SAV2002")),
new Branch("Delhi", List.of("CUR3001")),
new Branch("Chennai", List.of("SAV2003", "FD4001", "CUR3002"))
);
List<String> allAccounts = branches.stream()
.flatMap(b -> b.accountIds().stream())
.toList();
System.out.println("All accounts: " + allAccounts);
}
}
All accounts: [SAV2001, SAV2002, CUR3001, SAV2003, FD4001, CUR3002]
Use case 2: Split one value into many
List<String> allSymbols = List.of("RELIANCE,TCS,HDFC", "INFY,WIPRO").stream()
.flatMap(csv -> java.util.Arrays.stream(csv.split(",")))
.distinct()
.toList();
// [RELIANCE, TCS, HDFC, INFY, WIPRO]
Use case 3: Extract Optional values (Java 9)
import java.util.Optional;
import java.util.List;
List<String> emails = List.of(
Optional.of("rajesh@bank.com"), Optional.empty(), Optional.of("amit@bank.com")
).stream()
.flatMap(Optional::stream) // present -> value, empty -> gone
.toList();
// [rajesh@bank.com, amit@bank.com]
Use case 4: Merge multiple stream sources
import java.util.stream.Stream;
Stream<String> merged = Stream.of(
List.of("SAV2001", "SAV2002").stream(),
List.of("CUR3001", "CUR3002").stream()
).flatMap(s -> s);
// Stream<String> with all 4 accounts
Use case 5: Multi-level drill-down
package com.bank.service;
import java.util.List;
public class MultiLevelDrillDown {
record Customer(String name, List<Account> accounts) {}
record Account(String id, List<Transaction> transactions) {}
record Transaction(String txnId, String tag) {}
public static void main(String[] args) {
List<Customer> customers = List.of(
new Customer("Rajesh", List.of(
new Account("SAV2001", List.of(
new Transaction("T1", "TRANSFER"),
new Transaction("T2", "DEPOSIT")
))
)),
new Customer("Priya", List.of(
new Account("SAV2002", List.of(
new Transaction("T3", "WITHDRAWAL")
))
))
);
// 3-level drill-down with chained flatMap
List<String> allTags = customers.stream()
.flatMap(c -> c.accounts().stream())
.flatMap(a -> a.transactions().stream())
.map(Transaction::tag)
.distinct()
.toList();
System.out.println("Tags: " + allTags);
}
}
Tags: [TRANSFER, DEPOSIT, WITHDRAWAL]
What should a senior engineer know?
flatMap is the monadic bind. It applies to both Stream and Optional. Whenever a mapping function returns a container type (Stream, Optional, List), flatMap unwraps and merges. Without flatMap, the equivalent code is nested for-loops or filter+map+isPresent chains.
Visual Overview
Five flatMap use cases: ─────────────────────── 1. Nested: [[A,B], [C], [D,E]] → [A,B,C,D,E] 2. Split: "A,B,C" → [A, B, C] 3. Optional: [Opt(A), empty, Opt(C)] → [A, C] 4. Merge: stream1 + stream2 → merged stream 5. Drill: Customer → Account → Transaction → Tag
Key Takeaway
flatMap has five primary use cases: flattening nested collections, splitting values, extracting Optional values, merging stream sources, and multi-level drill-down. Each converts a nested structure into a flat one-dimensional stream.
The most powerful use case is multi-level drill-down: chaining flatMap calls to traverse customer to accounts to transactions to tags in a single declarative pipeline, replacing three nested for-loops.
Remember one sentence: flatMap opens, expands, and flattens: nested collections become flat, CSVs become tokens, and empty Optionals disappear.
---