В этой статье мы узнаем о вложенных коллекциях в Java и о том, как можно преобразовать их в плоские коллекции, используя различные подходы.
1. Что такое выравнивание?
В программировании, flatting a List означает объединение нескольких вложенных списков для создания одного списка. Flattened list состоит из всех элементов из вложенных списков.
List of nested lists: [[4, 5, 2], [1, 34, 23], [12], [10, 11, 15]];Flattened List: [4, 5, 2, 1, 34, 23, 12, 10, 11, 15];
В демонстрационных целях мы используем следующий вложенный список в каждом решении.
List<List<String>> nestedList = List.of(List.of("Alexandru", "John"),List.of("Emma","Andrew", "Luke"),List.of("Oliver"));
2. Использование пользовательской логики
Одно из самых простых решений — перебрать вложенные списки и добавить все элементы в новый объявленный плоский список. Мы можем добавить все элементы оптом с помощью функции addAll().
List<String> flatList = new ArrayList<>();nestedList.forEach(flatList::addAll);
Если исходный список также содержит отдельные элементы, то нам необходимо поставить условие if-else, чтобы сначала проверить тип элемента.
List<String> flatList = new ArrayList<>();for(Object item : nestedList) {if(item instanceof List<?>) {flatList.addAll(item)} else {flatList.add((String) item);}}
3. Использование потоков
API Java Streams предоставляет нам несколько интересных методов выравнивания вложенных связанных списков.
3.1 Использование flatMap()
Мы можем использовать функцию flatMap() с функцией mapper Collection::stream. При выполнении операции stream terminal каждый элемент flatMap() предоставляет отдельный поток. На заключительном этапе метод flatMap() преобразует все потоки в новый поток.
List<String> flatList = nestedList.stream().flatMap(Collection::stream).collect(Collectors.toList());
3.2 Использование reduce()
Редукция — это терминальный метод, агрегирующий поток. В методе reduce() мы передаем два аргумента функциям: первый называется identity, значение по умолчанию для редукции, а второй называется accumulator, который объединяет два значения.
В нашем случае, чтобы получить сглаженный List, нам нужно перебрать Stream и объединить последовательные списки в один для каждого последовательного вложенного List. Обратите внимание, что из-за дополнительных экземпляров ArrayList, созданных во время выполнения, этот метод не обеспечивает хорошей производительности для больших вложенных списков.
List<String> flatList = nestedList.stream().reduce(new ArrayList<>(),(l1, l2) -> {l1.addAll(l2);return l1;});
4. Использование коллекции Eclipse
Другой способ сделать список списков плоским — использовать метод flatCollect() из Eclipse Collections. Eclipse Collections — это фреймворк Java Collections, который имеет совместимые с JDK реализации List, Map и Set с богатым API.
Добавьте в приложение последнюю версию Eclipse Collection.
<dependency><groupId>org.eclipse.collections</groupId><artifactId>eclipse-collections</artifactId><version>11.1.0</version></dependency>
Мы будем использовать класс ListAdapter, который предоставляет обертку MutableList вокруг экземпляра интерфейса JDK Collections List и преобразует Java List в Eclipse Collection MutableList. После этого мы будем использовать его метод flatCollect(), который выровняет список списков.
List<String> flatList = ListAdapter.adapt(nestedList).flatCollect(e -> e);
5. Использование гуавы
Добавьте последнюю версию Guava из репозитория Maven.
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency>
Метод Iterables.concat() отлично подходит для создания плоских списков из вложенных списков. Этот метод объединяет несколько итерируемых объектов в один итерируемый объект.
Iterable<String> iterable = Iterables.concat(nestedList);List<String> flatList = Lists.newArrayList(iterable);
6. Заключение
Сглаживание вложенного списка может показаться сложным на первый взгляд, но это не так. Это можно легко сделать, используя только простую логику Java, потоки или даже внешние библиотеки коллекций.