Come posso unire due elenchi in Java?


749

Condizioni: non modificare gli elenchi originali; Solo JDK, nessuna libreria esterna. Punti bonus per una versione da 1 linea o JDK 1.3.

C'è un modo più semplice di:

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

5
Se si sta facendo questo esclusivamente per scopi di iterazione vedere un'altra domanda - ci sono google guava e Java 8 soluzioni stackoverflow.com/questions/4896662/...
Boris Treukhov

Java 8 soluzione con metodo di utilità: stackoverflow.com/a/37386846/1216775
akhil_mittal

Dopo aver letto alcune delle risposte, mi dispiace di averlo chiesto.
Anthony Rutledge,

Risposte:


591

In Java 8:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());

82
Accidenti, questa è una cosa in Java 8? Tecnicamente hai vinto, immagino, ma è un gran bel colpo :-)
Robert Atkins,

4
Per il lettore occasionale, ecco una soluzione più breve che utilizza anche Java _ Streams: stackoverflow.com/a/34090554/363573
Stephan

7
È brutto ma almeno fluente e può essere usato senza lambda multi linea. Vorrei davvero che ci fosse un add-fluente Tutto che ha restituito l'elenco concatinato.
Usman Ismail,

6
Immagino che valga la pena notare che è davvero facile ottenere un elenco distinto anche da questo, in questo modo:List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
Roger,

1
Alternativa al concat: stream of streamStream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser

569

Dalla parte superiore della mia testa, posso accorciarlo di una riga:

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

156
Mentre sei tecnicamente corretto, l'hai abbreviato di una riga, l'asimmetria di questo mi infastidisce. Abbastanza che sono più felice di "spendere" la linea in più.
Robert Atkins,

13
Non c'è un problema qui in cui l'array interale della newList verrà inizializzato alla dimensione di listOne e quindi dovrà potenzialmente espandersi quando si aggiungono tutti gli elementi da listTwo? Sarebbe meglio prendere la dimensione di ogni elenco e usarla per dimensionare il nuovo array?
Eric

2
Questa è stata la soluzione che ha funzionato meglio per me. Ho fatto un confronto sulle prestazioni di diverse soluzioni, questo è risultato vincitore, insieme alla creazione dell'elenco vuoto e quindi addAll()di entrambi. Ho provato tutti quelli che suggeriscono di non copiare gli elenchi e risultano avere un sacco di costi generali che non ci sono serviti questa volta.
manuelvigarcia,

bello ma puoi anche accorciarlo: List list = new ArrayList <> (list1) .addAll (list2);
velocità

1
@velocity: no, non funzionerà. addAll(Collection)restituisce a boolean.
Stijn Van Bael,

306

È possibile utilizzare la libreria commons-collection di Apache :

List<String> newList = ListUtils.union(list1, list2);

52
Bello, ma richiede beni comuni Apache. Ha specificato "nessuna biblioteca esterna"
Quantum7,

101
@ Quantum7, ancora utile per altre persone;) Inoltre, Apache Commons è anche una libreria esterna? Non inizio niente senza di essa!
tster

28
@Platinum No, secondo i documenti ListUtils.union è esattamente equivalente al codice di OP. Ma forse è fuorviante utilizzare un'operazione SET ("Unione") in un contesto di elenco. Posso vedere come potresti aspettarti che questo rimuova i duplicati o qualcosa del genere, ma sembra che il metodo non lo faccia.
Quantum7,

24
Evita le raccolte di Apache Commons. Non è dattiloscritto, non ci sono generici. Ottimo se usi Java 1.4, ma per Java 5 e versioni successive preferirei Google Guava.
Michael Piefel,

11
@MichaelPiefel L'ultima Apache Commons Collections 4 è sicura per i tipi. Con il riferimento al metodo Java 8, questo tipo di utility statiche diventa molto importante.
mingfai,

93

Uno dei tuoi requisiti è quello di preservare gli elenchi originali. Se crei un nuovo elenco e utilizzalo addAll(), stai effettivamente raddoppiando il numero di riferimenti agli oggetti nei tuoi elenchi. Ciò potrebbe causare problemi di memoria se gli elenchi sono molto grandi.

Se non è necessario modificare il risultato concatenato, è possibile evitarlo utilizzando un'implementazione dell'elenco personalizzato. La classe di implementazione personalizzata è più di una riga, ovviamente ... ma usarla è breve e dolce.

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);

15
Questa è la vera risposta a questa domanda.
Wouter Lievens,

9
Questa è una soluzione praticabile, ma si noti che se gli oggetti dell'elenco sottostante cambiano (elenco1, elenco2), il contenuto di questo elenco cambia. Potrebbe non essere possibile modificare un'istanza dell'Elenco composito non modificabile stesso, ma se è possibile ottenere un riferimento agli elenchi originali, è possibile. Anche per coloro che non hanno familiarità: il modificatore finale influenza solo il riferimento all'oggetto elenco stesso non può cambiare ma è ancora possibile cambiare il contenuto dell'elenco!
jwj,

3
@jwj tutti i punti molto buoni, grazie. Il nome della classe probabilmente merita qualche spiegazione. Vedo questa classe come qualcosa di molto simile al Collections.unmodifiableList()metodo, che racchiude un elenco per renderlo non modificabile. CompositeUnmodifiableListfa la stessa cosa, tranne che avvolge due elenchi e fornisce una vista concatenata. Anche tutti i punti che fai CompositeUnmodifiableListvalgono sono veri Collections.unmodifiableList().
Kevin K,

2
Il costruttore può prendereList<? extends E>
Patrick Parker il

84

Probabilmente non più semplice, ma intrigante e brutto:

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

Non usarlo nel codice di produzione ...;)


44
Brutto e malvagio, proprio come quasi ogni uso dell'inizializzazione della doppia parentesi graffa. È più breve, però;)
Jorn,

4
@MarnixKlooster: Eclipse sa che non dovresti usarlo e lo rende spiacevole da usare ;-)
Joachim Sauer,

20
Sebbene sia fisicamente una riga, non considero questo un "one-liner".
splungebob,

11
perché le persone odiano gli inizializzatori di blocchi anonimi
NimChimpsky,

18
@NimChimpsky Penso che sia principalmente perché non è solo un inizializzatore di blocchi anonimo, ma in realtà stai creando una sottoclasse anonima di ArrayList. Detto questo, se ti fidi dei risultati di questa domanda sull'inizializzazione di Double Brace , sembra che odiare il DBI sia principalmente una questione di gusto stilistico e micro-ottimizzazione. Per quanto ne so, non ci sono penalità importanti per farlo. Il rovescio della medaglia sarebbe se avessi mai provato a confrontare la sua classe perché non sarebbe ArrayList.
Patrick,

75

Un altro Java 8 one-liner:

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

Come bonus, dato che Stream.of()è variadico, puoi concatenare tutte le liste che desideri.

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

35
x -> x.stream()potrebbe essere sostituito con Collection::stream.
Martin,

10
... o anche con List::stream.
MC Emperor

73

Non più semplice, ma senza ridimensionare le spese generali:

List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);

55

Ho trovato questa domanda cercando di concatenare un numero arbitrario di liste, senza badare alle librerie esterne. Quindi, forse aiuterà qualcun altro:

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

Utile se si desidera applicare la stessa logica a un numero di raccolte diverse in una per ().


9
Ad esempio: Lists.newArrayList (Iterables.concat (list1, list2));
martedì

dovresti chiamare com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)invece di Iterables#concat(); perché in seguito copia ancora gli elementi nel link temporaneo!
bob

45

Java 8 ( Stream.ofe Stream.concat)

La soluzione proposta è per tre elenchi sebbene possa essere applicata anche per due elenchi. In Java 8 possiamo usare Stream.of o Stream.concat come:

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.concataccetta due flussi come input e crea un flusso pigramente concatenato i cui elementi sono tutti gli elementi del primo flusso seguiti da tutti gli elementi del secondo flusso. Dato che abbiamo tre liste, abbiamo usato questo metodo ( Stream.concat) due volte.

Possiamo anche scrivere una classe di utilità con un metodo che accetta un numero qualsiasi di elenchi (usando varargs ) e restituisce un elenco concatenato come:

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

Quindi possiamo utilizzare questo metodo come:

List<String> result3 = Utils.concatenateLists(list1,list2,list3);

Probabilmente sei disposto a dire List <String> result1 = Stream.concat (Stream.concat (list1.stream (), list2.stream ()), list3.stream ()). Collect (Collectors.toList ()); nel tuo primo operatore. Per favore, aggiustalo.
WebComer

44

Ecco una soluzione java 8 che utilizza due righe:

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

Tenere presente che questo metodo non deve essere utilizzato se

  • l'origine di newListnon è nota e potrebbe essere già condivisa con altri thread
  • il flusso che modifica newListè un flusso parallelo e l'accesso a newListnon è sincronizzato o thread-safe

a causa di considerazioni sugli effetti collaterali.

Entrambe le condizioni di cui sopra non si applicano al caso precedente di unire due elenchi, quindi questo è sicuro.

Sulla base di questa risposta a un'altra domanda.


12
Se non sbaglio questo non è in realtà raccomandato - docs.oracle.com/javase/8/docs/api/java/util/stream/… Consultare la sezione sugli effetti collaterali. > Gli effetti collaterali nei parametri comportamentali per le operazioni di streaming sono, in generale, scoraggiati, poiché spesso possono portare a inconsapevoli violazioni del requisito di apolidia, nonché ad altri pericoli per la sicurezza dei thread. Quindi in questo caso è meglio usare Collectors.toList ()
Anton Balaniuc

@AntonBalaniuc La domanda è se questo è davvero un effetto collaterale. A quel punto newListnon è osservabile da nessun altro thread. Ma hai ragione sul fatto che probabilmente non dovrebbe essere fatto se non è noto da dove newListprovenga il valore di (ad esempio se è newListstato passato come parametro.
SpaceTrucker

2
Sono curioso; perché .forEach(newList::addAll);invece di .collect(Collectors.toList());?
11684,

4
@ 11684 perché il collezionista avrebbe raccolto un List<List<Object>>. Quello che si può avere in mente è qualcosa di simile: stackoverflow.com/questions/189559/...
SpaceTrucker

@SpaceTrucker Oops, l'ho trascurato. Grazie per aver chiarito la mia confusione. Sì, avrei dovuto pensare flatMap.
11684,

34

Questo è semplice e solo una riga, ma aggiungerà il contenuto di listTwo a listOne. Hai davvero bisogno di mettere i contenuti in una terza lista?

Collections.addAll(listOne, listTwo.toArray());

11
Non modificare gli elenchi originali era uno dei criteri, ma questo è utile avere qui come esempio per situazioni in cui questo non è un vincolo.
Robert Atkins,

1
Grazie, o ancora più semplice listOne.addAll (listTwo)
Jay

27

Leggermente più semplice:

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

Ciò provocherebbe stringhe duplicate? Vuoi dire che una stringa che esiste in entrambi gli elenchi esisterà due volte nell'elenco risultante?
AgentKnopf,

4
@Zainodis Sì, potrebbero esserci dei duplicati. La Liststruttura non impone vincoli di unicità. Puoi rimuovere i duplicati facendo la stessa cosa con i set. Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
Patrick,

20

Un po 'più breve sarebbe:

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

17

È possibile creare il metodo di utilità Java 8 generico per concedere un numero qualsiasi di elenchi .

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

13

È possibile eseguire un allineamento se l'elenco di destinazione è stato dichiarato.

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


9

un'altra soluzione di linea che utilizza Java8stream, poiché la flatMapsoluzione è già pubblicata, ecco una soluzione senzaflatMap

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);

codice

    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);

produzione

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]

1
Aggiungerei che questa soluzione è probabilmente più performante di quella che utilizza flatMap, perché gli elenchi vengono ripetuti una sola volta quando vengono raccolti
Stefan Haberl,

7

Il più intelligente secondo me:

/**
 * @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;
}

3
Non dimenticare il @SafeVarargs!
Radon Rosborough,

6

Potresti farlo con un'importazione statica e una classe di supporto

nb la generazione di questa classe potrebbe probabilmente essere migliorata

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;
   }

}

Quindi puoi fare cose come

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);

In che modo è rilevante l'importazione statica o la classe di supporto?
shmosel,

6

Versione Java 8 con supporto per l'unione tramite chiave oggetto:

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());
}

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;
}

4

Usa una classe Helper.

Suggerisco:

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)));
}

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;
  }

2

Possiamo unire 2 liste usando java8 con 2 approcci.

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

1) Utilizzando concat:

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

2) Utilizzando flatMap:

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

1
Quando rispondi a una domanda di undici anni con altre trenta risposte, assicurati di indicare quali nuovi aspetti della domanda affronta la tua risposta e di notare se queste tecniche avrebbero funzionato quando è stata posta la domanda o se dipendono da funzionalità che sono state introdotto nel corso degli anni.
Jason Aller il

2

Quasi le risposte suggeriscono di utilizzare un ArrayList.

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

Preferisci usare un LinkedList per operazioni di aggiunta efficienti.

ArrayList add è O (1) ammortizzato, ma O (n) nel caso peggiore poiché l'array deve essere ridimensionato e copiato. Mentre LinkedList add è sempre costante O (1).

maggiori informazioni https://stackoverflow.com/a/322742/311420


0

Non sto affermando che sia semplice, ma hai menzionato il bonus per una linea ;-)

Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))

perché qualcuno non dovrebbe mai usare quelli?
David,

5
@David perché mirava ad essere utilizzato internamente in JDK. Se lo hai usato nel tuo codice, molto probabilmente il tuo codice non funzionerà su JDK / JRE non Sun (o non Oracle).
Adrian Shum,

@AdrianShum Esistono altri JDK / JRE oltre a Oracle? Questo mi sorprenderebbe. Anche se limitato alle funzionalità API più comuni, la ricostruzione di tutto ciò richiederebbe probabilmente secoli ...
Egor Hans,

1
C'è abbastanza JVM. Quello più comunemente visto nel mondo delle imprese dovrebbe essere quello IBM, che è, iirc, in bundle con websphere
Adrian Shum,

0

Non c'è molto vicino a una linea, ma penso che sia la più semplice:

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

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

0

Ecco un approccio che utilizza stream e java 8 se i tuoi elenchi hanno tipi diversi e vuoi combinarli con un elenco di un altro 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());
}

0

Se si desidera farlo staticamente è possibile quanto segue.

Gli esempi usano 2 EnumSet in ordine naturale (== Enum-order) A, Be poi si uniscono in un ALLelenco.

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);
                  }}
              );

Ciò creerebbe una nuova classe anonima. Approccio non raccomandato!
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;
    }

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.