Definition
- map(Function
) : transforms each element independently. N elements in, N elements out. - flatMap(Function
>) : transforms each element into a Stream and merges all resulting streams into one flat Stream. - Signature difference: map takes
T -> R, flatMap takesT -> Stream. - Nesting: map wraps (produces
Stream), flatMap unwraps (produces- >
Stream). - Zero elements: flatMap can return Stream.empty() for an element, effectively removing it.
- Optional.stream(): Java 9 feature; flatMap(Optional::stream) extracts present values and removes empties.
map is like the currency exchange counter at National Bank of India: each dollar cheque becomes a rupee cheque. One cheque in, one cheque out. flatMap is like splitting a corporate salary cheque into individual employee cheques: one corporate cheque in, 50 individual cheques out, all on the same conveyor belt.
Understanding the Concepts
What is the difference between map and flatMap?
Visual comparison
package com.bank.service;
import java.util.List;
import java.util.stream.Stream;
public class MapVsFlatMap {
record Customer(String name, List<String> accountIds) {}
public static void main(String[] args) {
List<Customer> customers = List.of(
new Customer("Rajesh", List.of("SAV2001", "CUR3001")),
new Customer("Priya", List.of("SAV2002")),
new Customer("Amit", List.of("SAV2003", "FD4001", "CUR3002"))
);
// map: Customer -> List<String> (NESTED!)
List<List<String>> nested = customers.stream()
.map(Customer::accountIds)
.toList();
System.out.println("map: " + nested);
// [[SAV2001, CUR3001], [SAV2002], [SAV2003, FD4001, CUR3002]]
// flatMap: Customer -> Stream<String> -> FLAT!
List<String> flat = customers.stream()
.flatMap(c -> c.accountIds().stream())
.toList();
System.out.println("flatMap: " + flat);
// [SAV2001, CUR3001, SAV2002, SAV2003, FD4001, CUR3002]
}
}
map: [[SAV2001, CUR3001], [SAV2002], [SAV2003, FD4001, CUR3002]]
flatMap: [SAV2001, CUR3001, SAV2002, SAV2003, FD4001, CUR3002]
Comparison table
| Aspect | map | flatMap | |--------|-----|--------| | Mapping | T -> R | T -> Stream<R> | | Cardinality | 1-to-1 | 1-to-many | | Result | Stream<R> (may be nested) | Stream<R> (always flat) | | Use case | Transform type | Flatten nested structures |
flatMap with Optional (Java 9)
package com.bank.service;
import java.util.List;
import java.util.Optional;
public class FlatMapOptional {
record Account(String id, Optional<String> email) {}
public static void main(String[] args) {
List<Account> accounts = List.of(
new Account("SAV2001", Optional.of("rajesh@bank.com")),
new Account("SAV2002", Optional.empty()),
new Account("CUR3001", Optional.of("amit@bank.com"))
);
// flatMap(Optional::stream) extracts present values, removes empties
List<String> emails = accounts.stream()
.map(Account::email)
.flatMap(Optional::stream)
.toList();
System.out.println("Emails: " + emails);
}
}
Emails: [rajesh@bank.com, amit@bank.com]
flatMap with String splitting
import java.util.Arrays;
import java.util.List;
List<String> allSymbols = List.of("RELIANCE,TCS,HDFC", "INFY,WIPRO").stream()
.flatMap(csv -> Arrays.stream(csv.split(",")))
.distinct()
.toList();
// [RELIANCE, TCS, HDFC, INFY, WIPRO]
What should a senior engineer know?
flatMap is the monadic bind operation. It parallels Optional.flatMap exactly: if a function returns an Optional, use flatMap to avoid nested Optional
Visual Overview
map (one-to-one, wraps): ──────────────────────── Customer A → [SAV2001, CUR3001] ← List (wrapped) Customer B → [SAV2002] ← List (wrapped) Customer C → [SAV2003, FD4001] ← List (wrapped) Result: Stream<List<String>> (3 elements, nested) flatMap (one-to-many, flattens): ──────────────────────────────── Customer A → SAV2001, CUR3001 ← individual elements Customer B → SAV2002 ← individual element Customer C → SAV2003, FD4001 ← individual elements Result: Stream<String> (5 elements, flat)
Key Takeaway
map transforms one-to-one: each element produces exactly one output. flatMap transforms one-to-many and flattens: each element produces a Stream that is merged into the main Stream. If map gives you Stream> (nested), flatMap gives you Stream
Use flatMap for nested structures (customers with accounts), string splitting (CSV to tokens), Optional extraction (flatMap(Optional::stream)), and merging multiple sources.
Remember one sentence: map wraps each result, flatMap unwraps and flattens, so use flatMap whenever you see nested Stream>.
---