Introduction
Java 8 introduced the Streams API, a powerful tool for processing collections of data in a declarative and efficient manner. Two fundamental operations within the Streams API are map
and flatMap
. While both transform streams, they do so in distinct ways, impacting how data is processed and the structure of the resulting stream. This tutorial will explain the differences between map
and flatMap
, demonstrating their use cases with practical examples.
Understanding the map
Operation
The map
operation transforms each element of a stream into another element, applying a provided function to each item. The function takes a single input and returns a single output. This creates a new stream with the transformed elements, maintaining a one-to-one correspondence between the input and output streams.
Syntax:
Stream<R> map(Function<? extends T, ? extends R> mapper)
Example:
Let’s say we have a list of strings and we want to convert each string to uppercase.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "cherry");
List<String> uppercaseWords = words.stream()
.map(String::toUpperCase) // Apply toUpperCase to each string
.collect(Collectors.toList());
System.out.println(uppercaseWords); // Output: [APPLE, BANANA, CHERRY]
}
}
In this example, the map
operation applies the String::toUpperCase
function to each element in the words
stream, creating a new stream containing the uppercase versions of the strings. The size and order of the elements remain consistent.
Understanding the flatMap
Operation
The flatMap
operation also transforms a stream, but with a key difference: it allows a function to return a stream for each element, and then flattens those streams into a single, combined stream. This is particularly useful when you need to process nested collections or generate multiple values from a single input element.
Syntax:
<R> Stream<R> flatMap(Function<? extends T, Stream<? extends R>> mapper)
Example:
Let’s consider a list of lists of integers. We want to create a single list containing all the integers from all the sublists.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> flattenedList = listOfLists.stream()
.flatMap(List::stream) // Convert each inner list to a stream and flatten
.collect(Collectors.toList());
System.out.println(flattenedList); // Output: [1, 2, 3, 4, 5, 6]
}
}
Here, flatMap
takes each inner List<Integer>
and converts it into a Stream<Integer>
. Then, it combines all these streams into a single Stream<Integer>
, effectively flattening the nested structure.
Key Differences Summarized
| Feature | map
| flatMap
|
|—————-|—————————————-|—————————————–|
| Transformation | One-to-one: Input -> Output | One-to-many: Input -> Stream -> Flattened Stream |
| Return Value | Single value for each input element | Stream of values for each input element |
| Flattening | No flattening occurs | Streams are flattened into a single stream |
| Use Cases | Simple element-wise transformations | Handling nested collections, generating multiple values |
Practical Applications
- Parsing data: You might use
flatMap
to parse a list of strings, where each string represents a comma-separated list of values. - Expanding collections: If you have a list of objects, each containing a list of related items,
flatMap
can extract all the related items into a single list. - Handling optional values: Combine streams of
Optional
objects withflatMap
to filter out empty optionals and extract their values.
Conclusion
Both map
and flatMap
are essential operations within the Java Streams API. Understanding their differences allows you to effectively process and transform data in a concise and expressive manner. Choose map
for simple one-to-one transformations and flatMap
when you need to handle collections of collections or generate multiple values from a single input.