web-dev-qa-db-fra.com

Comment joindre deux listes en Java?

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

Y a-t-il un moyen plus simple que:

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

De mémoire, je peux le raccourcir d’une ligne:

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

Dans Java 8:

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

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

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

Probablement pas plus simple, mais intrigant et moche:

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

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

82
volley

Une de vos exigences est de conserver les listes originales. Si vous créez une nouvelle liste et utilisez addAll(), vous doublez le nombre de références aux objets de 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 évidemment composée de plusieurs lignes, mais son utilisation est courte et agréable.

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);
81
Kevin K

Pas plus simple, mais sans redimensionnement:

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

Un autre Java 8 one-liner:

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

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

List<String> newList = Stream.of(listOne, listTwo, listThree)
                             .flatMap(x -> x.stream())
                             .collect(Collectors.toList());
59
Mark

Trouvé cette question cherchant à concaténer un nombre arbitraire de listes, sans se soucier des bibliothèques externes. Alors, peut-être que cela aidera 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 seule pour ().

50
Yuri Geinish

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 newList n'est pas connue et peut déjà être partagée avec d'autres threads
  • le flux qui modifie newList est un flux parallèle et l'accès à newList n'est pas synchronisé ni threadsafe

en raison d'effets indésirables.

Les deux conditions ci-dessus ne s'appliquent pas dans le cas ci-dessus d'adhésion à deux listes, c'est donc sûr.

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

37
SpaceTrucker

C'est simple et une seule 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());
32
ceklock

La solution proposée concerne trois listes, bien qu'elle puisse é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.concat prend deux flux en entrée et crée un flux paresseux 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 d’utilitaires avec une méthode prenant un nombre quelconque de listes (en utilisant varargs ) et renvoyant une liste concaténée sous la forme:

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

Légèrement plus simple:

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

Un peu plus court serait:

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

Vous pouvez faire une ligne si la liste cible est prédéclarée.

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

Vous pouvez créer votre méthode d’utilité générique Java 8 à concatrer nombre quelconque de listes.

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

une autre solution one-liner utilisant le flux Java8, puisque la solution flatMap est déjà affichée, voici une solution sans flatMap

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

sortie

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

Dans Java 8 (dans l'autre sens):

List<?> newList = 
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
7
Nitin Jain

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;
}
6
Olivier Faucheux

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);
5
Dave Cheney

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

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)));
}
4
alex
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;
}
3
martyglaubitz
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
Langusten Gustel

Pas du tout près d'un one-liner, mais je pense que c'est le plus simple:

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

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

Voici une approche utilisant des flux et Java 8 si vos listes ont des types différents 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
shinzou

Si vous voulez le faire de manière statique, vous pouvez ce qui suit.

Les exemples utilisent 2 EnumSets dans l'ordre naturel (== Enum-order) A, B et sont ensuite joints dans une liste ALL.

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);
                  }}
              );
0
Jan Weitz

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

Collection mergedList = Collections.list(new Sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))
0
ddimitrov
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;
    }

}
0
benez