How to flatten a list of lists
Short answer
flatMap() replaces each element with a stream and joins those streams into one:
void main() {
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3), List.of(4, 5, 6));
List<Integer> flat = nested.stream().flatMap(List::stream).toList();
IO.println(flat);
}Output:
[1, 2, 3, 4, 5, 6]A nested field
Often the list lives inside an object — for example, products inside orders:
record Order(int id, List<String> products) {}
void main() {
var orders = List.of(new Order(1, List.of("tea", "cake")), new Order(2, List.of("coffee", "tea")));
var allProducts = orders.stream().flatMap(o -> o.products().stream()).distinct().toList();
IO.println(allProducts);
}Output:
[tea, cake, coffee]Arrays
For an array of arrays use Arrays::stream:
void main() {
String[][] table = {{"a", "b"}, {"c"}};
IO.println(Arrays.stream(table).flatMap(Arrays::stream).toList());
}Output:
[a, b, c]Words from lines
flatMap() is handy for splitting lines into words and collecting them together:
void main() {
var lines = List.of("to be", "or not to be");
var words = lines.stream().flatMap(line -> Arrays.stream(line.split(" "))).toList();
IO.println(words);
}Output:
[to, be, or, not, to, be]Common mistake
Using map() instead of flatMap(). You get a stream of streams, not a stream of elements:
void main() {
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3));
IO.println(nested.stream().map(List::stream).toList());
}Output:
[java.util.stream.ReferencePipeline$Head@2c34f934, java.util.stream.ReferencePipeline$Head@12d3a4e9]The object addresses after @ will differ for you.
Functions used in this recipe
- Stream.flatMap() — Turns each element into a stream and joins all streams into one.
- Stream.toList() — Gathers stream elements into an unmodifiable list.