Overview
Both map() and flatMap() are intermediate operations in Java’s Stream API. They are used to transform elements of a stream, but they differ in what they return and how they handle nested structures.
1. map() – Transform Each Element One-to-One
Purpose:
- Applies a function to each element of the stream.
- The function transforms the element and returns a new value.
- The number of output elements remains the same as the number of input elements.
Method Signature:
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
Characteristics:
- Input: Stream of type
T - Output: Stream of type
R(same number of elements) - Common use: Convert
StringtoInteger, extract fields from objects, etc.
Example 1: Convert List of Strings to Uppercase
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperNames); // [ALICE, BOB, CHARLIE]
Example 2: Extract field from objects
List<Person> people = List.of(
new Person("Alice", 25),
new Person("Bob", 30)
);
List<String> names = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
System.out.println(names); // [Alice, Bob]
2. flatMap() – Transform and Flatten One-to-Many
Purpose:
- Applies a function to each element that returns a stream (or collection).
- Then flattens the results into a single stream.
- It’s used to flatten nested structures.
Method Signature:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
Characteristics:
- Input: Stream of type
T - Function returns: Stream of
R - Output: Flattened stream of all elements from all the returned streams
- Useful when you need to transform one-to-many (e.g., splitting strings into words)
Example 1: Splitting Strings into Words
List<String> lines = Arrays.asList("Java is fun", "Streams are powerful");
List<String> words = lines.stream()
.flatMap(line -> Arrays.stream(line.split(" ")))
.collect(Collectors.toList());
System.out.println(words);
// [Java, is, fun, Streams, are, powerful]
If you used map() instead:
List<Stream<String>> mapped = lines.stream()
.map(line -> Arrays.stream(line.split(" ")))
.collect(Collectors.toList());
Now you have a List<Stream<String>>, not a List<String>. You’d need to flatten it manually if you want to work with the individual words.
Example 2: Flatten List of Lists
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d"),
Arrays.asList("e", "f")
);
List<String> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flatList); // [a, b, c, d, e, f]
With map(), you’d get a Stream<List<String>>, which isn’t what you want if you’re trying to flatten it.
3. Summary Table
| Feature | map() | flatMap() |
|---|---|---|
| Return Type | Stream<R> | Stream<R> |
| Function Returns | One object per input (R) | A Stream<R> (or collection) per input |
| Output Structure | 1-to-1 mapping | 1-to-many mapping + flattening |
| Use Case | Transforming elements | Flattening nested streams or collections |
| Keeps Nesting? | Yes | No (flattens the structure) |
4. When to Use
- Use
map()when:- You want to transform data
- Each element maps to one result
- Use
flatMap()when:- You want to flatten nested structures (lists of lists, streams of streams, etc.)
- Each element maps to multiple results
5. Visual Illustration
Consider this input:
List<String> list = Arrays.asList("one,two", "three,four");
With map():
Stream<String[]> mapped = list.stream()
.map(s -> s.split(","));
Result type: Stream<String[]> — A stream of arrays
With flatMap():
Stream<String> flatMapped = list.stream()
.flatMap(s -> Arrays.stream(s.split(",")));
Result type: Stream<String> — Flattened stream of individual strings
6. Advanced Example: Nested Objects
class Department {
String name;
List<Employee> employees;
public List<Employee> getEmployees() {
return employees;
}
}
Extract all employees across departments
List<Department> departments = // some list...
List<Employee> allEmployees = departments.stream()
.flatMap(dept -> dept.getEmployees().stream())
.collect(Collectors.toList());
If you used map() instead, you’d get List<Stream<Employee>>, not List<Employee>.
Conclusion
Understanding map() vs flatMap() is crucial for effective Java Stream API usage:
- Use
map()for simple transformation - Use
flatMap()for flattening nested data structures flatMap()is essentiallymap()+flatten
