Encodera

Thanks for reading!

Donate to the site — we work hard for you.

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.

Thanks for reading!

Donate to the site — we work hard for you.