Java: combinar varias colecciones

1. Información general

En este tutorial, ilustraremos cómo concatenar varias colecciones en una colección lógica.

Exploraremos cinco enfoques diferentes: dos usando Java 8, uno usando Guava, uno usando Apache Commons Collections y uno usando solo el estándar Java 7 SDK.

En los ejemplos que siguen, consideremos las siguientes colecciones:

Collection collectionA = Arrays.asList("S", "T"); Collection collectionB = Arrays.asList("U", "V");

2. Utilizando Java 8 Stream API

La interfaz Stream en la API de Java proporciona métodos útiles que facilitan el procesamiento de colecciones. Echemos un vistazo a dos de sus métodos, concat () y flatMap () , que se utilizan para combinar colecciones.

Una vez que obtiene un Stream , puede realizar operaciones agregadas en él.

2.1. Usando el método concat ()

El método estático concat () combina dos Streams de forma lógica creando un Stream concatenado de forma perezosa cuyos elementos son todos los elementos del primer Stream seguidos por todos los elementos del segundo Stream .

En el siguiente ejemplo, combinemos collectionA y collectionB usando el método concat () :

Stream combinedStream = Stream.concat( collectionA.stream(), collectionB.stream());

Si necesita combinar más de dos Streams , puede invocar el método concat () nuevamente desde la invocación original:

Stream combinedStream = Stream.concat( Stream.concat(collectionA.stream(), collectionB.stream()), collectionC.stream());

Es importante tener en cuenta que los flujos de Java 8 no son reutilizables, por lo que debe tener esto en cuenta al asignarlos a variables.

2.2. Uso de la flatMap () Método

El método flatMap () devuelve un Stream después de reemplazar cada elemento de este Stream con el contenido de un Stream mapeado que se produce aplicando la función de mapeo proporcionada a cada elemento.

El siguiente ejemplo demuestra la fusión de colecciones utilizando el método flatMap () . Inicialmente, obtiene un Stream cuyos elementos son las dos colecciones, y luego aplana el Stream antes de recopilarlo en una lista combinada:

Stream combinedStream = Stream.of(collectionA, collectionB) .flatMap(Collection::stream); Collection collectionCombined = combinedStream.collect(Collectors.toList());

3. Usar guayaba

La biblioteca Guava de Google proporciona varios métodos convenientes para operar en colecciones y se puede usar con Java 6 o posterior.

3.1. Usando el método Iterables.concat ()

El método Iterables.concat () es uno de los métodos convenientes de Guava que se utiliza para fusionar colecciones:

Iterable combinedIterables = Iterables.unmodifiableIterable( Iterables.concat(collectionA, collectionA));

El Iterable que se devuelve se puede convertir en una colección:

Collection collectionCombined = Lists.newArrayList(combinedIterables);

3.2. Dependencia de Maven

Agregue la siguiente dependencia a su archivo pom.xml de Maven para incluir la biblioteca Guava en su proyecto:

 com.google.guava guava 20.0 

Puede encontrar la última versión de la biblioteca de Guava en el repositorio de Maven Central.

4. Uso de colecciones de Apache Commons

Apache Commons Collections es otra biblioteca de utilidades que ayuda a trabajar con las distintas colecciones. La biblioteca proporciona dos métodos de utilidad que se pueden utilizar para combinar colecciones. En esta sección, entendamos cómo funcionan estos métodos.

4.1. Usando el método IterableUtils.chainedIterable ()

La clase IterableUtils proporciona métodos de utilidad y decoradores para instancias Iterable . Proporciona el método chainedIterable () , que se puede usar para combinar varios Iterables en uno solo.

Iterable combinedIterables = IterableUtils.chainedIterable( collectionA, collectionB);

4.2. Uso del método CollectionUtils.union ()

Los métodos de utilidad y los decoradores para las instancias de Collection los proporciona la clase CollectionUtils . El método union () de esta clase devuelve una colección que contiene la unión de las instancias Iterables dadas .

Iterable combinedIterables = CollectionUtils.union( collectionA, collectionB);

En el caso del método union () , la cardinalidad de cada elemento en la colección devuelta será igual al máximo de la cardinalidad de ese elemento en los dos iterables dados . Esto significa que la colección combinada solo consta de los elementos de la primera colección y los elementos de la segunda colección que no estaban presentes en la primera.

4.3. Dependencia de Maven

Agregue la siguiente dependencia a su archivo pom.xml de Maven para incluir la biblioteca Apache Commons Collections en su proyecto:

 org.apache.commons commons-collections4 4.1 

Puede encontrar la última versión de la biblioteca Apache Commons en el repositorio de Maven Central.

5. Usando Java 7

Si todavía usa Java 7 y desea evitar bibliotecas de terceros como Guava, puede usar el método addAll () para combinar elementos de múltiples colecciones, o puede escribir sus propios métodos de utilidad para combinar iterables .

5.1. Usando el método addAll ()

Of course the simplest solution for combining collections is using the addAll() method, as in the following List example, however it is worth noting that this method creates a new collection with additional references to the same objects that are in the first two collections:

List listC = new ArrayList(); listC.addAll(listA); listC.addAll(listB);

5.2. Writing a Custom concat() Method

The below example defines a concat() method that accepts two Iterables and returns a merged Iterable object:

public static  Iterable concat( Iterable i1, Iterable i2) { return new Iterable() { public Iterator iterator() { return new Iterator() { Iterator 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(); } }; } }; }

The concat() method can be invoked by passing the two collections as its arguments:

Iterable combinedIterables = concat(collectionA, collectionB); Collection collectionCombined = makeListFromIterable(combinedIterables);

Si necesita que Iterable esté disponible como una Lista , también puede usar el método makeListFromIterable () que crea una Lista usando los miembros de Iterable :

public static  List makeListFromIterable(Iterable iter) { List list = new ArrayList(); for (E item : iter) { list.add(item); } return list; }

6. Conclusión

El artículo discutió varias formas diferentes de combinar dos colecciones de manera lógica en Java sin crear referencias adicionales a los objetos que contienen.

El código de este tutorial está disponible en Github.