Как преобразовать список списков в список в Java 8?
Вопрос: Как объединить List<List<Object>>
в List<Object>
с использованием возможностей Java 8?
У меня есть структура данных в виде List<List<Object>>
, и я хотел бы преобразовать её в List<Object>
, содержащий все объекты из вложенных списков, сохраняя порядок итерации.
Как можно сделать это с помощью функциональных возможностей Java 8, таких как стримы? Буду благодарен за пример кода или объяснение решения!
5 ответ(ов)
flatMap
действительно является более элегантным и читаемым способом достижения этой цели, но есть и другие способы, которые могут быть использованы для достижения того же результата.
Например, вы можете использовать метод collect
для объединения всех списков в один. Пример кода, который вы привели, демонстрирует, как это можно сделать:
List<List<Object>> listOfList = ... // заполните список
List<Object> collect =
listOfList.stream()
.collect(ArrayList::new, List::addAll, List::addAll);
В этом коде мы создаем новый ArrayList
и добавляем все элементы из каждого списка, используя метод addAll
. Это работает, но, как вы правильно заметили, существует более простой способ — использование flatMap
.
Для сравнения, вот как это можно сделать с помощью flatMap
:
List<Object> collect =
listOfList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Здесь мы используем flatMap
, чтобы "развернуть" каждый вложенный список и объединить все элементы в один поток. Затем мы собираем их в новый список с помощью Collectors.toList()
. Этот подход более лаконичен и легче воспринимается.
Чтобы преобразовать List<List>
в List
, вы можете использовать метод flatMap
в Java Streams. Вот пример кода:
listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
Посмотрите на следующий пример:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class Example {
public static void main(String[] args) {
List<List<String>> listOfLists = Collections.singletonList(Arrays.asList("a", "b", "c"));
List<String> list = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
System.out.println("listOfLists => " + listOfLists);
System.out.println("list => " + list);
}
}
Этот код выводит:
listOfLists => [[a, b, c]]
list => [a, b, c]
В этом примере мы берем список списков listOfLists
, используем метод stream
для его преобразования в поток, а затем применяем flatMap
, чтобы развернуть вложенные списки в один поток. Наконец, мы собираем все элементы в новый список с помощью collect(Collectors.toList())
.
В Python такое возможно сделать с помощью List Comprehension:
list_of_lists = [['Roopa','Roopi','Tabu', 'Soudipta'],[180.0, 1231, 2112, 3112], [130], [158.2], [220.2]]
flatten = [val for sublist in list_of_lists for val in sublist]
print(flatten)
Этот фрагмент кода печатает:
['Roopa', 'Roopi', 'Tabu', 'Soudipta', 180.0, 1231, 2112, 3112, 130, 158.2, 220.2]
В данном случае мы используем генератор списка, который проходит по каждому подсписку и добавляет элементы в результирующий flatten
список.
Как уже упоминал @Saravana:
flatMap лучше, но есть и другие способы достижения той же цели.
Вот несколько способов объединения списков из Stream<List<T>>
:
private <T> List<T> mergeOne(Stream<List<T>> listStream) {
return listStream.flatMap(List::stream).collect(Collectors.toList());
}
private <T> List<T> mergeTwo(Stream<List<T>> listStream) {
List<T> result = new ArrayList<>();
listStream.forEach(result::addAll);
return result;
}
private <T> List<T> mergeThree(Stream<List<T>> listStream) {
return listStream.reduce(new ArrayList<>(), (l1, l2) -> {
l1.addAll(l2);
return l1;
});
}
private <T> List<T> mergeFour(Stream<List<T>> listStream) {
return listStream.reduce((l1, l2) -> {
List<T> l = new ArrayList<>(l1);
l.addAll(l2);
return l;
}).orElse(new ArrayList<>());
}
private <T> List<T> mergeFive(Stream<List<T>> listStream) {
return listStream.collect(ArrayList::new, List::addAll, List::addAll);
}
Таким образом, существует несколько способов достижения одной и той же цели, и выбор способа зависит от конкретной ситуации и требований к производительности.
Конечно! Вот перевод вашего текста на русский в стиле ответа на StackOverflow:
Я хотел бы объяснить еще один сценарий, связанный с классом List<Documents>
, который содержит в себе несколько других списков документов, таких как List<Excel>
, List<Word>
, List<PowerPoint>
. Структура будет выглядеть следующим образом:
class A {
List<Documents> documentList;
}
class Documents {
List<Excel> excels;
List<Word> words;
List<PowerPoint> ppt;
}
Теперь, если вы хотите итерироваться только по документам Excel
из списка документов, вы можете сделать что-то похожее на следующее:
Код будет выглядеть так:
List<Documents> documentList = new A().getDocumentList();
// Проверяем, что documentList не равен null
Optional<Excel> excelOptional = documentList.stream()
.map(doc -> doc.getExcels())
.flatMap(List::stream).findFirst();
if (excelOptional.isPresent()) {
Excel exl = excelOptional.get();
// Теперь получите значение, которое вам нужно.
}
Надеюсь, это поможет кому-то при программировании!
Расширение ответа Эрана, который был самым популярным. Если у вас есть несколько уровней списков, вы можете продолжать использовать метод flatMap для их распаковки.
Кроме того, это также позволяет вам удобно фильтровать элементы по мере продвижения вниз по уровням, если это необходимо.
Например:
List<List<List<List<List<List<Object>>>>>> multiLayeredList = ...
List<Object> objectList = multiLayeredList
.stream()
.flatMap(someList1 -> someList1
.stream()
.filter(...Optional...))
.flatMap(someList2 -> someList2
.stream()
.filter(...Optional...))
.flatMap(someList3 -> someList3
.stream()
.filter(...Optional...))
...
.collect(Collectors.toList());
Это будет аналогично использованию операторов SELECT внутри других операторов SELECT в SQL.
Java 8: Уникальные элементы по свойству (Distinct by property)
Инициализация ArrayList в одну строчку
Как установить Java 8 на Mac
Преобразование 'ArrayList<String>' в 'String[]' в Java
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?