Comment joindre deux listes en Java?


749

Conditions: ne modifiez pas les listes originales; JDK uniquement, pas de bibliothèques externes. Points bonus pour un one-liner ou une version JDK 1.3.

Existe-t-il un moyen plus simple que:

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

5
Si vous faites cela uniquement à des fins d'itération, consultez une autre question - il existe des solutions google guava et java 8 stackoverflow.com/questions/4896662/…
Boris Treukhov

Solution Java 8 avec méthode utilitaire: stackoverflow.com/a/37386846/1216775
akhil_mittal

Après avoir lu certaines des réponses, je suis désolé d'avoir demandé.
Anthony Rutledge

Réponses:


591

En Java 8:

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

82
Gawd, c'est une chose en Java 8? Techniquement, vous gagnez, je suppose, mais c'est une sacrée longue file :-)
Robert Atkins

4
Pour le lecteur occasionnel, voici une solution plus courte utilisant également Java _ Streams: stackoverflow.com/a/34090554/363573
Stephan

7
Il est laid mais au moins fluide et peut être utilisé sans lambdas multi-lignes. Je souhaite vraiment qu'il y ait un addAll fluide qui a renvoyé la liste concaténée.
Usman Ismail

6
Je suppose que cela vaut la peine de noter qu'il est très facile d'obtenir une liste distincte de cela aussi, comme ceci:List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
Roger

1
Alternative à la concaténation: flux de fluxStream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser

569

Du haut de ma tête, je peux le raccourcir d'une ligne:

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

156
Bien que vous soyez techniquement correct, vous l'avez raccourci d'une ligne, l'asymétrie de cela me dérange. Assez que je suis plus heureux de "dépenser" la ligne supplémentaire.
Robert Atkins

13
N'y a-t-il pas un problème ici où le tableau interne de newList sera initialisé à la taille de listOne et devra ensuite potentiellement se développer lors de l'ajout de tous les éléments de listTwo? Serait-il préférable de prendre la taille de chaque liste et de l'utiliser pour dimensionner le nouveau tableau?
Eric

2
C'était la solution qui me convenait le mieux. J'ai fait une comparaison sur les performances des différentes solutions qui ont remporté le prix, ainsi que la création de la liste vide, puis addAll()des deux. J'ai essayé tous ceux qui suggèrent de ne pas copier les listes et ils entraînent beaucoup de frais généraux dont nous n'avions pas besoin cette fois.
manuelvigarcia

sympa mais vous pouvez même le raccourcir: List list = new ArrayList <> (list1) .addAll (list2);
vitesse

1
@velocity: non, ça ne va pas marcher. addAll(Collection)renvoie a boolean.
Stijn Van Bael

306

Vous pouvez utiliser la bibliothèque Apache commons-collections :

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

52
Bien, mais nécessite Apache Common. Il a précisé 'pas de bibliothèques externes'
Quantum7

101
@ Quantum7, toujours utile pour d'autres personnes;) De plus, apache commons est-il même une bibliothèque externe? Je ne commence rien sans ça!
tster

28
@Platinum Non, selon la documentation ListUtils.union est exactement équivalent au code OP. Mais il est peut-être trompeur d'utiliser une opération SET ("Union") dans un contexte de liste. Je peux voir comment vous pourriez vous attendre à ce que cela supprime les doublons ou quelque chose, mais il semble que la méthode ne le fasse pas.
Quantum7

24
Évitez les collections Apache Commons. Ce n'est pas sûr pour les types, il n'y a pas de génériques. Idéal si vous utilisez Java 1.4, mais pour Java 5 et supérieur, je préférerais Google Guava.
Michael Piefel

11
@MichaelPiefel La dernière Apache Commons Collections 4 est sécurisée. Avec la référence de méthode Java 8, ce type d'utilitaires statiques devient très important.
mingfai

93

L'une de vos exigences est de conserver les listes originales. Si vous créez une nouvelle liste et utilisez addAll(), vous doublez effectivement le nombre de références aux objets dans vos listes. Cela pourrait entraîner des problèmes de mémoire si vos listes sont très volumineuses.

Si vous n'avez pas besoin de modifier le résultat concaténé, vous pouvez éviter cela en utilisant une implémentation de liste personnalisée. La classe d'implémentation personnalisée est plus d'une ligne, évidemment ... mais son utilisation est courte et douce.

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

Usage:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);

15
Telle est la vraie réponse à cette question.
Wouter Lievens

9
Il s'agit d'une solution réalisable, mais notez que si les objets de liste sous-jacents changent (list1, list2), le contenu de cette liste change. Vous ne pourrez peut-être pas modifier une instance de CompositeUnmodifiableList lui-même, mais si vous pouvez obtenir une référence aux listes d'origine, vous le pouvez. Aussi pour ceux qui ne sont pas familiers: le dernier modificateur affecte juste la référence à l'objet liste lui-même ne peut pas changer mais il est toujours possible que le contenu de la liste change!
jwj

3
@jwj tous de très bons points, merci. Le nom de la classe mérite probablement quelques explications. Je vois cette classe comme faisant quelque chose de très similaire à la Collections.unmodifiableList()méthode, qui encapsule une liste pour la rendre non modifiable. CompositeUnmodifiableListfait la même chose, sauf qu'il encapsule deux listes et fournit une vue concaténée. Tous les points que vous soulevez CompositeUnmodifiableListsont également valables Collections.unmodifiableList().
Kevin K

2
Le constructeur peut prendreList<? extends E>
Patrick Parker

84

Probablement pas plus simple, mais intrigant et laid:

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

Ne l'utilisez pas dans le code de production ...;)


44
Laid et le mal, tout comme presque n'importe quelle utilisation de l'initialisation à double accolade. C'est plus court, cependant;)
Jorn

4
@MarnixKlooster: Eclipse sait que vous ne devriez pas l'utiliser et le rend désagréable à utiliser ;-)
Joachim Sauer

20
Bien qu'il s'agisse physiquement d'une seule ligne, je ne la considère pas comme une "ligne unique".
splungebob

11
pourquoi les gens détestent les initialiseurs de blocs anonymes
NimChimpsky

18
@NimChimpsky Je pense que c'est principalement parce que ce n'est pas seulement un initialiseur de bloc anonyme, mais que vous créez en fait une sous-classe anonyme d'ArrayList. Cela étant dit, si vous faites confiance aux résultats de cette question d'initiation Double Brace , cela donne l'impression que la haine de DBI est principalement une question de goût stylistique et de micro-optimisation. Pour autant que je sache, il n'y a pas de sanctions majeures pour le faire. L'inconvénient sournois serait que vous essayiez de comparer sa classe car ce ne sera pas ArrayList.
Patrick

75

Une autre ligne unique Java 8:

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

En prime, comme Stream.of()c'est variadic, vous pouvez concaténer autant de listes que vous le souhaitez.

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

35
x -> x.stream()pourrait être remplacé par Collection::stream.
Martin

10
... ou même avec List::stream.
MC Emperor

73

Pas plus simple, mais sans redimensionner la surcharge:

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

55

Trouvé cette question cherchant à concaténer une quantité arbitraire de listes, sans se soucier des bibliothèques externes. Donc, cela aidera peut-être quelqu'un d'autre:

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

Utile si vous souhaitez appliquer la même logique à plusieurs collections différentes en une pour ().


9
Par exemple: Lists.newArrayList (Iterables.concat (list1, list2));
meilechh

vous devez appeler com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)au lieu de Iterables#concat(); car ces derniers copient toujours les éléments dans le lien temporaire!
bob

45

Java 8 ( Stream.ofet Stream.concat)

La solution proposée concerne trois listes, mais elle peut également s'appliquer à deux listes. Dans Java 8, nous pouvons utiliser Stream.of ou Stream.concat comme:

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.concatprend deux flux en entrée et crée un flux paresseusement concaténé dont les éléments sont tous les éléments du premier flux suivis de tous les éléments du second flux. Comme nous avons trois listes, nous avons utilisé cette méthode ( Stream.concat) deux fois.

Nous pouvons également écrire une classe utilitaire avec une méthode qui prend n'importe quel nombre de listes (en utilisant des varargs ) et retourne une liste concaténée comme:

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

Ensuite, nous pouvons utiliser cette méthode comme:

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

Vous êtes probablement disposé à dire List <String> result1 = Stream.concat (Stream.concat (list1.stream (), list2.stream ()), list3.stream ()). Collect (Collectors.toList ()); dans votre premier opérateur. Regle-le, s'il te plait.
WebComer

44

Voici une solution java 8 utilisant deux lignes:

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

Sachez que cette méthode ne doit pas être utilisée si

  • l'origine de newListn'est pas connue et elle peut déjà être partagée avec d'autres threads
  • le flux qui modifie newListest un flux parallèle et l'accès à newListn'est pas synchronisé ou threadsafe

en raison de considérations d'effets secondaires.

Les deux conditions ci-dessus ne s'appliquent pas au cas ci-dessus de rejoindre deux listes, c'est donc sans danger.

Basé sur cette réponse à une autre question.


12
Si je ne me trompe pas, ce n'est en fait pas recommandé - docs.oracle.com/javase/8/docs/api/java/util/stream/… Veuillez consulter la section des effets secondaires. > Les effets secondaires des paramètres de comportement pour les opérations de streaming sont, en général, découragés, car ils peuvent souvent conduire à des violations involontaires de l'apatridie, ainsi qu'à d'autres dangers liés à la sécurité des threads. Donc, dans ce cas, il est préférable d'utiliser Collectors.toList ()
Anton Balaniuc

@AntonBalaniuc La question est de savoir si c'est vraiment un effet secondaire. À ce stade, newListn'est plus observable par aucun autre thread. Mais vous avez raison, cela ne devrait probablement pas être fait si on ne sait pas d'où vient la valeur de newList(par exemple, si elle a newListété passée en paramètre.
SpaceTrucker

2
Je suis curieux; pourquoi .forEach(newList::addAll);au lieu de .collect(Collectors.toList());?
11684

4
@ 11684 car le collectionneur collecterait a List<List<Object>>. Ce que vous pourriez avoir à l'esprit est quelque chose comme ceci: stackoverflow.com/questions/189559/…
SpaceTrucker

@SpaceTrucker Oups, j'ai oublié cela. Merci d'avoir dissipé ma confusion. Oui, j'aurais dû penser flatMap.
11684

34

C'est simple et juste une ligne, mais ajoutera le contenu de listTwo à listOne. Avez-vous vraiment besoin de mettre le contenu dans une troisième liste?

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

11
Ne pas modifier les listes d'origine était l'un des critères, mais il est utile d'avoir ici comme exemple pour les situations où ce n'est pas une contrainte.
Robert Atkins

1
Merci, ou encore plus simple listOne.addAll (listTwo)
Jay

27

Un peu plus simple:

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

Cela entraînerait-il des chaînes en double? Cela signifie qu'une chaîne qui existe dans les deux listes existera deux fois dans la liste résultante?
AgentKnopf

4
@Zainodis Oui, il pourrait y avoir des doublons. La Liststructure n'impose aucune contrainte d'unicité. Vous pouvez supprimer les dupes en faisant la même chose avec les ensembles. Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
Patrick

20

Un peu plus court serait:

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

17

Vous pouvez créer votre méthode utilitaire générique Java 8 pour concatrier n'importe quel nombre de listes .

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

13

Vous pouvez faire un oneliner si la liste cible est pré-déclarée.

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


9

une autre solution de liner en utilisant Java8stream, puisque la flatMapsolution est déjà publiée, voici une solution sansflatMap

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

ou

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

code

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

production

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

1
J'ajouterais que cette solution est probablement plus performante que celle qui utilise flatMap, car les listes ne sont itérées qu'une seule fois lors de leur collecte
Stefan Haberl

7

Le plus intelligent à mon avis:

/**
 * @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
N'oubliez pas le @SafeVarargs!
Radon Rosborough du

6

Vous pouvez le faire avec une importation statique et une classe d'assistance

nb la génération de cette classe pourrait probablement être améliorée

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

}

Ensuite, vous pouvez faire des choses comme

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

En quoi l'importation statique ou la classe d'assistance sont-elles pertinentes?
shmosel

6

Version Java 8 avec prise en charge de la jonction par clé d'objet:

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

Utilisez une classe d'assistance.

Je suggère:

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

Nous pouvons joindre 2 listes en utilisant java8 avec 2 approches.

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

1) Utilisation de concat:

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

2) Utilisation de flatMap:

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

1
Lorsque vous répondez à une question de onze ans avec trente autres réponses, assurez-vous de souligner les nouveaux aspects de la question auxquels votre réponse répond et de noter si ces techniques auraient fonctionné lorsque la question a été posée, ou si elles dépendent de fonctionnalités qui ont été introduit au fil des ans.
Jason Aller

2

Presque des réponses suggèrent d'utiliser une ArrayList.

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

Préférez utiliser une LinkedList pour des opérations d'ajout efficaces.

ArrayList add est O (1) amorti, mais O (n) dans le pire des cas car le tableau doit être redimensionné et copié. Alors que LinkedList add est toujours constant O (1).

plus d'infos https://stackoverflow.com/a/322742/311420


0

Je ne prétends pas que c'est simple, mais vous avez mentionné le bonus pour les one-liners ;-)

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

pourquoi ne devrait-on jamais les utiliser?
David

5
@David car il visait à être utilisé en interne dans JDK. Si vous l'avez utilisé dans votre code, votre code ne fonctionnera probablement pas sur JDK / JRE non Sun (ou non Oracle maintenant).
Adrian Shum

@AdrianShum Y a-t-il d'autres JDK / JRE qu'Oracle? Cela me surprendrait. Même si elle est limitée aux fonctionnalités d'API les plus courantes, la reconstruction de tout cela prendrait probablement des âges ...
Egor Hans

1
Il y a beaucoup de JVM. Le plus couramment vu dans le monde de l'entreprise devrait être celui d'IBM qui est, iirc, fourni avec la sphère Web
Adrian Shum

0

Pas du tout près d'une ligne, mais je pense que c'est la plus simple:

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

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

0

Voici une approche utilisant des flux et java 8 si vos listes ont différents types et que vous souhaitez les combiner à une liste d'un autre type.

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

Si vous souhaitez le faire de manière statique, vous pouvez procéder comme suit.

Les exemples utilisent 2 EnumSets dans l'ordre naturel (== Enum-order) A, Bet les rejoignent ensuite dans une ALLliste.

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

Cela créerait une nouvelle classe annonymous. Approche non recommandée!
kravemir du

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

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.