Перейти к основному содержимому

Java — объединить несколько коллекций

· 5 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

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 .