Как превратить список списков в один список
Короткий ответ
flatMap() заменяет каждый элемент потоком и склеивает эти потоки в один:
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);
}Результат:
[1, 2, 3, 4, 5, 6]Вложенное поле
Часто список лежит внутри объекта — например, товары внутри заказов:
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);
}Результат:
[tea, cake, coffee]Массивы
Для массива массивов используйте Arrays::stream:
void main() {
String[][] table = {{"a", "b"}, {"c"}};
IO.println(Arrays.stream(table).flatMap(Arrays::stream).toList());
}Результат:
[a, b, c]Слова из строк
flatMap() удобен, чтобы разбить строки на слова и собрать их вместе:
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);
}Результат:
[to, be, or, not, to, be]Частая ошибка
Использовать map() вместо flatMap(). Получается поток потоков, а не поток элементов:
void main() {
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3));
IO.println(nested.stream().map(List::stream).toList());
}Результат:
[java.util.stream.ReferencePipeline$Head@2c34f934, java.util.stream.ReferencePipeline$Head@12d3a4e9]Адреса объектов после @ у вас будут другими.
Функции из этого ответа
- Stream.flatMap() — Превращает каждый элемент в поток и склеивает все потоки в один.
- Stream.toList() — Собирает элементы потока в неизменяемый список.