1. Обзор
В этом руководстве мы покажем, как объединить несколько коллекций в одну логическую коллекцию.
Мы рассмотрим пять различных подходов: два с использованием Java 8, один с использованием Guava, один с использованием Apache Commons Collections и один с использованием только стандартного Java 7 SDK.
В следующих примерах рассмотрим следующие коллекции:
Collection<String> collectionA = Arrays.asList("S", "T");
Collection<String> collectionB = Arrays.asList("U", "V");
2. Использование потокового
API Java 8
Интерфейс Stream
в Java API предоставляет полезные методы, упрощающие обработку коллекций. Давайте рассмотрим два его метода — concat()
и flatMap()
— которые используются для объединения коллекций.
Получив Stream
, вы можете выполнять над ним агрегатные операции.
2.1. Использование метода concat()
``
Статический метод concat()
логически объединяет два потока
, создавая лениво объединенный поток
, элементами которого являются все элементы первого потока
, за которыми следуют все элементы второго потока
.
В приведенном ниже примере давайте объединим collectionA
и collectionB
с помощью метода concat()
:
Stream<String> combinedStream = Stream.concat(
collectionA.stream(),
collectionB.stream());
Если вам нужно объединить более двух потоков
, вы можете снова вызвать метод concat()
из исходного вызова:
Stream<String> combinedStream = Stream.concat(
Stream.concat(collectionA.stream(), collectionB.stream()),
collectionC.stream());
Важно отметить, что потоки
Java 8 нельзя использовать повторно, поэтому вы должны учитывать это при назначении их переменным.
2.2. Использование метода flatMap()
``
Метод flatMap()
возвращает поток
после замены каждого элемента этого потока
содержимым сопоставленного потока
, созданного путем применения предоставленной функции сопоставления к каждому элементу.
Пример ниже демонстрирует слияние коллекций с помощью метода flatMap()
. Сначала вы получаете поток
, элементами которого являются две коллекции, а затем сглаживаете поток
, прежде чем собрать его в объединенный список:
Stream<String> combinedStream = Stream.of(collectionA, collectionB)
.flatMap(Collection::stream);
Collection<String> collectionCombined =
combinedStream.collect(Collectors.toList());
3. Использование гуавы
Библиотека Guava от Google предоставляет несколько удобных методов для работы с коллекциями и может использоваться с Java 6 или более поздней версии.
3.1. Использование метода Iterables.concat()
Метод Iterables.concat()
— один из удобных методов Guava, который используется для слияния коллекций:
Iterable<String> combinedIterables = Iterables.unmodifiableIterable(
Iterables.concat(collectionA, collectionA));
Возвращаемый объект Iterable
можно преобразовать в коллекцию :
Collection<String> collectionCombined = Lists.newArrayList(combinedIterables);
3.2. Зависимость от Maven
Добавьте следующую зависимость в файл Maven pom.xml
, чтобы включить библиотеку Guava в свой проект:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
Вы можете найти последнюю версию библиотеки Guava в репозитории Maven Central .
4. Использование коллекций Apache Commons
Apache Commons Collections — это еще одна библиотека утилит, помогающих работать с различными коллекциями. Библиотека предоставляет два служебных метода, которые можно использовать для объединения коллекций. В этом разделе давайте разберемся, как работают эти методы.
4.1. Использование метода IterableUtils.chainedIterable()
Класс IterableUtils
предоставляет служебные методы и декораторы для экземпляров Iterable .
Он предоставляет метод chainedIterable()
, который можно использовать для объединения нескольких Iterable
в один.
Iterable<String> combinedIterables = IterableUtils.chainedIterable(
collectionA, collectionB);
4.2. Использование метода CollectionUtils.union()
Служебные методы и декораторы для экземпляров Collection
предоставляются классом CollectionUtils .
Метод union()
из этого класса возвращает коллекцию
, содержащую объединение заданных экземпляров Iterable .
Iterable<String> combinedIterables = CollectionUtils.union(
collectionA, collectionB);
В случае метода union()
мощность каждого элемента в возвращаемой коллекции будет равна максимальной мощности этого элемента в двух данных Iterables
. Это означает, что объединенная коллекция состоит только из элементов первой коллекции и элементов второй коллекции, которых не было в первой.
4.3. Зависимость от Maven
Добавьте следующую зависимость в файл Maven pom.xml
, чтобы включить библиотеку Apache Commons Collections в свой проект:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
Вы можете найти последнюю версию библиотеки Apache Commons в репозитории Maven Central .
5. Использование Java 7
Если вы все еще используете Java 7 и хотите избежать сторонних библиотек, таких как Guava, вы можете использовать метод addAll()
для объединения элементов из нескольких коллекций или написать свои собственные служебные методы для объединения Iterables
.
5.1. Использование метода addAll()
Конечно, самым простым решением для объединения коллекций является использование метода addAll()
, как в следующем примере List
, однако стоит отметить, что этот метод создает новую коллекцию с дополнительными ссылками на те же объекты, что и в первых двух коллекциях:
List<String> listC = new ArrayList<>();
listC.addAll(listA);
listC.addAll(listB);
5.2. Написание собственного метода concat()
В приведенном ниже примере определяется метод concat()
, который принимает два объекта Iterable
и возвращает объединенный объект Iterable
:
public static <E> Iterable<E> concat(
Iterable<? extends E> i1,
Iterable<? extends E> i2) {
return new Iterable<E>() {
public Iterator<E> iterator() {
return new Iterator<E>() {
Iterator<? extends E> listIterator = i1.iterator();
Boolean checkedHasNext;
E nextValue;
private boolean startTheSecond;
void theNext() {
if (listIterator.hasNext()) {
checkedHasNext = true;
nextValue = listIterator.next();
} else if (startTheSecond)
checkedHasNext = false;
else {
startTheSecond = true;
listIterator = i2.iterator();
theNext();
}
}
public boolean hasNext() {
if (checkedHasNext == null)
theNext();
return checkedHasNext;
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
checkedHasNext = null;
return nextValue;
}
public void remove() {
listIterator.remove();
}
};
}
};
}
Метод concat()
можно вызвать, передав две коллекции в качестве аргументов:
Iterable<String> combinedIterables = concat(collectionA, collectionB);
Collection<String> collectionCombined = makeListFromIterable(combinedIterables);
Если вам нужно, чтобы Iterable
был доступен в виде List
, вы также можете использовать метод makeListFromIterable()
, который создает List
, используя элементы Iterable
:
public static <E> List<E> makeListFromIterable(Iterable<E> iter) {
List<E> list = new ArrayList<E>();
for (E item : iter) {
list.add(item);
}
return list;
}
6. Заключение
В статье обсуждалось несколько различных способов логического объединения двух коллекций в Java без создания дополнительных ссылок на содержащиеся в них объекты.
Код для этого руководства доступен на Github .