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);
De mémoire, je peux le raccourcir d’une ligne:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Dans Java 8:
List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.toList());
Vous pouvez utiliser la bibliothèque Apache commons-collections :
List<String> newList = ListUtils.union(list1, list2);
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 ...;)
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);
Pas plus simple, mais sans redimensionnement:
List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);
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());
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 ().
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
newList
n'est pas connue et peut déjà être partagée avec d'autres threadsnewList
est un flux parallèle et l'accès à newList
n'est pas synchronisé ni threadsafeen 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.
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());
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);
Légèrement plus simple:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Un peu plus court serait:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Vous pouvez faire une ligne si la liste cible est prédéclarée.
(newList = new ArrayList<String>(list1)).addAll(list2);
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());
}
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]
Dans Java 8 (dans l'autre sens):
List<?> newList =
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
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;
}
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);
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());
}
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)));
}
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;
}
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;
}
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);
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());
}
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);
}}
);
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(),
...
}))
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;
}
}