¿Cómo unir dos listas en Java?

749

Condiciones: no modifique las listas originales; JDK solamente, no hay bibliotecas externas. Puntos de bonificación para una versión de una línea o JDK 1.3.

¿Hay una manera más simple que:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
Robert Atkins
fuente
55
Si está haciendo esto únicamente para propósitos de iteración, vea otra pregunta: hay soluciones de guayaba y java 8 en google stackoverflow.com/questions/4896662/…
Boris Treukhov
Solución Java 8 con método de utilidad: stackoverflow.com/a/37386846/1216775
akhil_mittal
Después de leer algunas de las respuestas, lamento haberlo preguntado.
Anthony Rutledge

Respuestas:

591

En Java 8:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());
Dale Emery
fuente
82
Gawd, ¿eso es algo en Java 8? Técnicamente, usted gana, supongo, pero esa es una gran cola :-)
Robert Atkins
44
Para el lector casual, aquí hay una solución más corta que también utiliza Java _ Streams: stackoverflow.com/a/34090554/363573
Stephan
77
Es feo pero al menos es fluido y se puede usar sin lambdas de líneas múltiples. Realmente desearía que hubiera un addAll fluido que devolviera la lista concatenada.
Usman Ismail
66
Supongo que vale la pena señalar que también es muy fácil obtener una lista distinta de esto, así:List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
Roger
1
Alternativa al concat: flujo de flujosStream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser
569

Desde la parte superior de mi cabeza, puedo acortarlo en una línea:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
AdamC
fuente
156
Si bien es técnicamente correcto, lo ha acortado en una línea, la asimetría de esto me molesta. Suficiente que estoy más feliz de "gastar" la línea extra.
Robert Atkins
13
¿No hay un problema aquí donde la matriz interna de newList se inicializará al tamaño de listOne y luego tendrá que expandirse potencialmente al agregar todos los elementos de listTwo? ¿Sería mejor tomar el tamaño de cada lista y usarlo para dimensionar la nueva matriz?
Eric
2
Esta fue la solución que mejor funcionó para mí. Hice una comparación sobre el rendimiento de diferentes soluciones que resultó ganador, junto con la creación de la lista vacía y luego addAll()de ambas. Intenté todos los que sugieren no copiar las listas y resultan tener una sobrecarga que no necesitábamos esta vez.
manuelvigarcia
agradable, pero incluso puede hacerlo más corto: List list = new ArrayList <> (list1) .addAll (list2);
velocidad
1
@velocity: no, eso no va a funcionar. addAll(Collection)devuelve a boolean.
Stijn Van Bael
306

Puede usar la biblioteca de colecciones comunes de Apache :

List<String> newList = ListUtils.union(list1, list2);
Guillermo
fuente
52
Agradable, pero requiere apache commons. Él especificó 'no hay bibliotecas externas'
Quantum7
101
@ Quantum7, sigue siendo útil para otras personas;) Además, ¿es apache commons incluso una biblioteca externa? ¡No comienzo nada sin eso!
tster
28
@Platinum No, de acuerdo con la documentación ListUtils.union es exactamente equivalente al código de OP. Pero quizás sea engañoso usar una operación SET ("Unión") en un contexto de lista. Puedo ver cómo podría esperar que esto elimine duplicados o algo así, pero parece que el método no hace eso.
Quantum7
24
Evite las colecciones de Apache Commons. No es seguro, no hay genéricos. Genial si usa Java 1.4, pero para Java 5 y superior, preferiría Google Guava.
Michael Piefel
11
@MichaelPiefel La última colección 4 de Apache Commons es de tipo seguro. Con la referencia del método Java 8, este tipo de utilidades estáticas se vuelven muy importantes.
mingfai
93

Uno de sus requisitos es preservar las listas originales. Si crea una nueva lista y la usa addAll(), efectivamente está duplicando el número de referencias a los objetos en sus listas. Esto podría provocar problemas de memoria si sus listas son muy grandes.

Si no necesita modificar el resultado concatenado, puede evitarlo utilizando una implementación de lista personalizada. La clase de implementación personalizada es más de una línea, obviamente ... pero usarla es corta y agradable.

CompositeUnmodifiableList.java:

public class CompositeUnmodifiableList<E> extends AbstractList<E> {

    private final List<E> list1;
    private final List<E> list2;

    public CompositeUnmodifiableList(List<E> list1, List<E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    @Override
    public E get(int index) {
        if (index < list1.size()) {
            return list1.get(index);
        }
        return list2.get(index-list1.size());
    }

    @Override
    public int size() {
        return list1.size() + list2.size();
    }
}

Uso:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
Kevin K
fuente
15
Esta es la verdadera respuesta a esta pregunta.
Wouter Lievens
99
Esta es una solución viable, pero tenga en cuenta que si los objetos de la lista subyacente cambian (list1, list2), el contenido de esta lista cambia. Es posible que no pueda modificar una instancia de CompositeUnmodifiableList , pero si puede obtener una referencia a las listas originales, puede hacerlo. También para aquellos que no están familiarizados: ¡el modificador final solo afecta la referencia al objeto de la lista en sí mismo, pero no puede cambiar el contenido de la lista!
jwj
3
@jwj todos muy buenos puntos, gracias. El nombre de la clase probablemente merece alguna explicación. Veo que esta clase está haciendo algo muy similar al Collections.unmodifiableList()método, que envuelve una lista para que no se pueda modificar. CompositeUnmodifiableListhace lo mismo, excepto que envuelve dos listas y proporciona una vista concatenada. Todos los puntos que haces CompositeUnmodifiableListtambién son ciertos Collections.unmodifiableList().
Kevin K
2
El constructor puede tomarList<? extends E>
Patrick Parker
84

Probablemente no más simple, pero intrigante y feo:

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };

No lo use en el código de producción ...;)

voleo
fuente
44
Feo y malvado, como casi cualquier uso de inicialización de doble llave. Sin embargo, es más corto;)
Jorn
44
@MarnixKlooster: Eclipse sabe que no debes usarlo y lo hace desagradable de usar ;-)
Joachim Sauer
20
Aunque físicamente es una línea, no considero que sea un "one-liner".
splungebob
11
¿Por qué la gente odia los inicializadores de bloque anónimos
NimChimpsky
18
@NimChimpsky Creo que es principalmente porque no es solo un inicializador de bloque anónimo, sino que en realidad estás creando una subclase anónima de ArrayList. Dicho esto, si confía en los resultados de esta pregunta de Iniciación de doble refuerzo , parece que odiar a DBI es principalmente una cuestión de gusto estilístico y micro-optimización. Por lo que puedo decir, no hay sanciones importantes por hacerlo. El inconveniente astuto sería si alguna vez intentas comparar su clase porque no será ArrayList.
Patrick
75

Otro Java 8 one-liner:

List<String> newList = Stream.of(listOne, listTwo)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());

Como Stream.of()beneficio adicional, dado que es variable, puede concatenar tantas listas como desee.

List<String> newList = Stream.of(listOne, listTwo, listThree)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());
marca
fuente
35
x -> x.stream()podría ser reemplazado por Collection::stream.
Martin
10
... o incluso con List::stream.
MC Emperor
73

No es más simple, pero sin cambiar el tamaño de la sobrecarga:

List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);
Martín
fuente
55

Encontré esta pregunta buscando concatenar una cantidad arbitraria de listas, sin importar bibliotecas externas. Entonces, quizás ayude a alguien más:

com.google.common.collect.Iterables#concat()

Útil si desea aplicar la misma lógica a varias colecciones diferentes en una para ().

Yuri Geinish
fuente
99
Por ejemplo: Lists.newArrayList (Iterables.concat (list1, list2));
meilechh
deberías llamar en com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)lugar de Iterables#concat(); ¡porque el posterior todavía copia elementos en el enlace temporal!
bob
45

Java 8 ( Stream.ofy Stream.concat)

La solución propuesta es para tres listas, aunque también se puede aplicar para dos listas. En Java 8 podemos hacer uso de Stream.of o Stream.concat como:

List<String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList());
List<String> result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList());

Stream.concattoma dos secuencias como entrada y crea una secuencia concatenada perezosamente cuyos elementos son todos los elementos de la primera secuencia seguidos de todos los elementos de la segunda secuencia. Como tenemos tres listas, hemos usado este método ( Stream.concat) dos veces.

También podemos escribir una clase de utilidad con un método que tome cualquier número de listas (usando varargs ) y devuelva una lista concatenada como:

public static <T> List<T> concatenateLists(List<T>... collections) {
        return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList()); 
}

Entonces podemos hacer uso de este método como:

List<String> result3 = Utils.concatenateLists(list1,list2,list3);
akhil_mittal
fuente
Probablemente esté dispuesto a decir List <String> result1 = Stream.concat (Stream.concat (list1.stream (), list2.stream ()), list3.stream ()). Collect (Collectors.toList ()); en tu primer operador. Por favor, arreglalo.
WebComer
44

Aquí hay una solución de Java 8 que usa dos líneas:

List<Object> newList = new ArrayList<>();
Stream.of(list1, list2).forEach(newList::addAll);

Tenga en cuenta que este método no debe usarse si

  • newListse desconoce el origen de y ya se puede compartir con otros hilos
  • la secuencia que se modifica newListes una secuencia paralela y el acceso newListno está sincronizado o es seguro

debido a consideraciones de efectos secundarios.

Las dos condiciones anteriores no se aplican al caso anterior de unir dos listas, por lo que esto es seguro.

Basado en esta respuesta a otra pregunta.

SpaceTrucker
fuente
12
Si no me equivoco, esto en realidad no se recomienda: docs.oracle.com/javase/8/docs/api/java/util/stream/… Consulte la sección de efectos secundarios. > En general, se desaconsejan los efectos secundarios en los parámetros de comportamiento para transmitir las operaciones, ya que a menudo pueden conducir a violaciones involuntarias del requisito de apatridia, así como a otros riesgos de seguridad de hilos. Entonces, en este caso, es mejor usar Collectors.toList ()
Anton Balaniuc
@AntonBalaniuc La pregunta es si esto es realmente un efecto secundario. En ese punto newListno es observable por ningún otro hilo. Pero tiene razón en que esto probablemente no debería hacerse si se desconoce de dónde newListproviene el valor de (por ejemplo, si newListse pasó como parámetro.
SpaceTrucker
2
Soy curioso; ¿Por qué en .forEach(newList::addAll);lugar de .collect(Collectors.toList());?
11684
44
@ 11684 porque el recolector recolectaría a List<List<Object>>. Lo que puede tener en mente es algo como esto: stackoverflow.com/questions/189559/…
SpaceTrucker
@SpaceTrucker Vaya, lo pasé por alto. Gracias por aclarar mi confusión. Sí, debería haber estado pensando flatMap.
11684
34

Esto es simple y solo una línea, pero agregará el contenido de listTwo to listOne. ¿Realmente necesitas poner el contenido en una tercera lista?

Collections.addAll(listOne, listTwo.toArray());
ceklock
fuente
11
No modificar las listas originales fue uno de los criterios, pero es útil tenerlo aquí como ejemplo para situaciones en las que eso no es una restricción.
Robert Atkins
1
Gracias, o incluso más simple listOne.addAll (listTwo)
Jay
27

Ligeramente más simple:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Tim
fuente
¿Causaría esto cadenas duplicadas? ¿Significa que una cadena que existe en ambas listas existirá dos veces en la lista resultante?
AgentKnopf
44
@Zainodis Sí, podría haber duplicados. La Listestructura no impone restricciones de unicidad. Puede eliminar los engaños haciendo lo mismo con los conjuntos. Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
Patrick
20

Un poco más corto sería:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Jorn
fuente
17

Puede crear su método de utilidad genérico Java 8 para concatenar cualquier cantidad de listas .

@SafeVarargs
public static <T> List<T> concat(List<T>... lists) {
    return Stream.of(lists).flatMap(List::stream).collect(Collectors.toList());
}
Daniel Hári
fuente
13

Puede hacer un oneliner si la lista de objetivos está predeclarada.

(newList = new ArrayList<String>(list1)).addAll(list2);
determinar
fuente
11

En Java 8 (al revés):

List<?> newList = 
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
Nitin Jain
fuente
Este enfoque ya lo sugiere esta respuesta: stackoverflow.com/a/37386846
Miles
9

otra solución de un revestimiento utilizando Java8flujo, ya que la flatMapsolución ya está publicada, aquí hay una solución sinflatMap

List<E> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);

o

List<E> ints = Stream.of(list1, list2).collect(ArrayList::new, List::addAll, List::addAll);

código

    List<List<Integer>> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
    List<Integer> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
    System.out.println(lol);
    System.out.println(li);

salida

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
Saravana
fuente
1
Agregaría que esta solución es probablemente más eficaz que la que usa flatMap, porque las listas solo se repiten una vez cuando se recopilan
Stefan Haberl,
7

El más inteligente en mi opinión:

/**
 * @param smallLists
 * @return one big list containing all elements of the small ones, in the same order.
 */
public static <E> List<E> concatenate (final List<E> ... smallLists)
{
    final ArrayList<E> bigList = new ArrayList<E>();
    for (final List<E> list: smallLists)
    {
        bigList.addAll(list);
    }
    return bigList;
}
Olivier Faucheux
fuente
3
¡No te olvides de la @SafeVarargs!
Radon Rosborough
6

Podrías hacerlo con una importación estática y una clase auxiliar

nb el generification de esta clase probablemente podría mejorarse

public class Lists {

   private Lists() { } // can't be instantiated

   public static List<T> join(List<T>... lists) {
      List<T> result = new ArrayList<T>();
      for(List<T> list : lists) {
         result.addAll(list);
      }
      return results;
   }

}

Entonces puedes hacer cosas como

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);
Dave Cheney
fuente
¿Cómo es relevante la importación estática o la clase auxiliar?
shmosel
6

Versión de Java 8 con soporte para unirse por clave de objeto:

public List<SomeClass> mergeLists(final List<SomeClass> left, final List<SomeClass> right, String primaryKey) {
    final Map<Object, SomeClass> mergedList = new LinkedHashMap<>();

    Stream.concat(left.stream(), right.stream())
        .map(someObject -> new Pair<Object, SomeClass>(someObject.getSomeKey(), someObject))
        .forEach(pair-> mergedList.put(pair.getKey(), pair.getValue()));

    return new ArrayList<>(mergedList.values());
}
cslysy
fuente
4
public static <T> List<T> merge(List<T>... args) {
    final List<T> result = new ArrayList<>();

    for (List<T> list : args) {
        result.addAll(list);
    }

    return result;
}
Martyglaubitz
fuente
4

Usa una clase de ayuda.

Yo sugiero:

public static <E> Collection<E> addAll(Collection<E> dest, Collection<? extends E>... src) {
    for(Collection<? extends E> c : src) {
        dest.addAll(c);
    }

    return dest;
}

public static void main(String[] args) {
    System.out.println(addAll(new ArrayList<Object>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    // does not compile
    // System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6)));
}
alex
fuente
3
public static <T> List<T> merge(@Nonnull final List<T>... list) {
    // calculate length first
    int mergedLength = 0;
    for (List<T> ts : list) {
      mergedLength += ts.size();
    }

    final List<T> mergedList = new ArrayList<>(mergedLength);

    for (List<T> ts : list) {
      mergedList.addAll(ts);
    }

    return mergedList;
  }
Langusten Gustel
fuente
2

Podemos unir 2 listas usando java8 con 2 enfoques.

    List<String> list1 = Arrays.asList("S", "T");
    List<String> list2 = Arrays.asList("U", "V");

1) Usando concat:

    List<String> collect2 = Stream.concat(list1.stream(), list2.stream()).collect(toList());
    System.out.println("collect2 = " + collect2); // collect2 = [S, T, U, V]

2) Usando flatMap:

    List<String> collect3 = Stream.of(list1, list2).flatMap(Collection::stream).collect(toList());
    System.out.println("collect3 = " + collect3); // collect3 = [S, T, U, V]
Himank Batra
fuente
1
Cuando responda una pregunta de once años con otras treinta respuestas, asegúrese de señalar qué aspectos nuevos de la pregunta aborda su respuesta, y tenga en cuenta si estas técnicas habrían funcionado cuando se hizo la pregunta, o si dependen de las características que se han utilizado. introducido a lo largo de los años.
Jason Aller
2

Casi todas las respuestas sugieren usar una ArrayList.

List<String> newList = new LinkedList<>(listOne);
newList.addAll(listTwo);

Prefiere usar un LinkedList para operaciones de adición eficientes.

ArrayList add es O (1) amortizado, pero O (n) en el peor de los casos ya que la matriz debe ser redimensionada y copiada. Mientras que LinkedList add siempre es constante O (1).

más información https://stackoverflow.com/a/322742/311420

Raymond Chenon
fuente
0

No estoy afirmando que sea simple, pero mencionaste la bonificación por frases ingeniosas ;-)

Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))
ddimitrov
fuente
¿Por qué alguien nunca debería usar esos?
David
55
@David porque pretendía ser utilizado internamente en JDK. Si usó eso en su código, es probable que su código no se ejecute en JDK / JRE que no sean Sun (o que no sean Oracle ahora).
Adrian Shum
@AdrianShum ¿Hay otros JDK / JRE que Oracle? Eso me sorprendería. Incluso si se limita a la funcionalidad API más común, la reconstrucción de todo eso probablemente llevaría años ...
Egor Hans
1
Hay bastante JVM. El más comúnmente visto en el mundo empresarial debería ser el de IBM, que está, iirc, incluido con la web
Adrian Shum
0

De ninguna manera cerca de una línea, pero creo que este es el más simple:

List<String> newList = new ArrayList<String>(l1);
newList.addAll(l2);

for(String w:newList)
        System.out.printf("%s ", w);
nirmal
fuente
0

Aquí hay un enfoque que usa streams y java 8 si sus listas tienen diferentes tipos y desea combinarlas en una lista de otro tipo.

public static void main(String[] args) {
    List<String> list2 = new ArrayList<>();
    List<Pair<Integer, String>> list1 = new ArrayList<>();

    list2.add("asd");
    list2.add("asdaf");
    list1.add(new Pair<>(1, "werwe"));
    list1.add(new Pair<>(2, "tyutyu"));

    Stream stream = Stream.concat(list1.stream(), list2.stream());

    List<Pair<Integer, String>> res = (List<Pair<Integer, String>>) stream
            .map(item -> {
                if (item instanceof String) {
                    return new Pair<>(0, item);
                }
                else {
                    return new Pair<>(((Pair<Integer, String>)item).getKey(), ((Pair<Integer, String>)item).getValue());
                }
            })
            .collect(Collectors.toList());
}
Shinzou
fuente
0

Si desea hacer esto estáticamente, puede hacer lo siguiente.

Los ejemplos usan 2 EnumSets en orden natural (== Enum-order) A, By luego se unen en una ALLlista.

public static final EnumSet<MyType> CATEGORY_A = EnumSet.of(A_1, A_2);
public static final EnumSet<MyType> CATEGORY_B = EnumSet.of(B_1, B_2, B_3);

public static final List<MyType> ALL = 
              Collections.unmodifiableList(
                  new ArrayList<MyType>(CATEGORY_A.size() + CATEGORY_B.size())
                  {{
                      addAll(CATEGORY_A);
                      addAll(CATEGORY_B);
                  }}
              );
Jan Weitz
fuente
Esto crearía una nueva clase anónima. Enfoque no recomendado!
kravemir
-3
import java.util.AbstractList;
import java.util.List;


/**
 * The {@code ConcatList} is a lightweight view of two {@code List}s.
 * <p>
 * This implementation is <em>not</em> thread-safe even though the underlying lists can be.
 * 
 * @param <E>
 *            the type of elements in this list
 */
public class ConcatList<E> extends AbstractList<E> {

    /** The first underlying list. */
    private final List<E> list1;
    /** The second underlying list. */
    private final List<E> list2;

    /**
     * Constructs a new {@code ConcatList} from the given two lists.
     * 
     * @param list1
     *            the first list
     * @param list2
     *            the second list
     */
    public ConcatList(final List<E> list1, final List<E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    @Override
    public E get(final int index) {
        return getList(index).get(getListIndex(index));
    }

    @Override
    public E set(final int index, final E element) {
        return getList(index).set(getListIndex(index), element);
    }

    @Override
    public void add(final int index, final E element) {
        getList(index).add(getListIndex(index), element);
    }

    @Override
    public E remove(final int index) {
        return getList(index).remove(getListIndex(index));
    }

    @Override
    public int size() {
        return list1.size() + list2.size();
    }

    @Override
    public boolean contains(final Object o) {
        return list1.contains(o) || list2.contains(o);
    }

    @Override
    public void clear() {
        list1.clear();
        list2.clear();
    }

    /**
     * Returns the index within the corresponding list related to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the index of the underlying list
     */
    private int getListIndex(final int index) {
        final int size1 = list1.size();
        return index >= size1 ? index - size1 : index;
    }

    /**
     * Returns the list that corresponds to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the underlying list that corresponds to that index
     */
    private List<E> getList(final int index) {
        return index >= list1.size() ? list2 : list1;
    }

}
benez
fuente