Comment joindre deux listes en Java?
Conditions: ne pas modifier les listes originales; JDK seulement, 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);
29 réponses
du haut de ma tête, je peux la raccourcir d'une ligne:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
En Java 8:
List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.toList());
vous pouvez utiliser le Apache commons-collections bibliothèque:
List<String> newList = ListUtils.union(list1, list2);
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... ;)
une de vos exigences est de préserver les listes originales. Si vous créez une nouvelle liste et utilisez addAll()
, vous doublez effectivement le nombre de références aux objets de vos listes. Cela pourrait conduire à des problèmes de mémoire si vos listes sont très grandes.
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 custom implementation est évidemment plus d'une ligne...mais l'utiliser est court et sucrée.
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();
}
}
Utilisation:
List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
pas plus simple, mais sans redimensionnement au-dessus de la tête:
List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);
a trouvé cette question à la recherche de concaténer quantité arbitraire de listes, sans faire attention aux bibliothèques externes. Ainsi, peut-être cela aidera quelqu'un d'autre:
com.google.common.collect.Iterables#concat()
utile si vous voulez appliquer la même logique à un certain nombre de collections différentes dans un pour().
Another Java 8 one-liner:
List<String> newList = Stream.of(listOne, listTwo)
.flatMap(x -> x.stream())
.collect(Collectors.toList());
comme bonus, puisque 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());
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 elle peut déjà être partagée avec d'autres fils - le flux qui modifie
newList
est un flux parallèle et l'accès ànewList
n'est pas synchronisé ou threadsafe
dû à un effet secondaire considération.
les Deux conditions ci-dessus ne s'appliquent pas dans le cas ci-dessus de fusionner deux listes, c'est sûr.
basé sur cette réponse à une autre question.
c'est simple et seulement 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());
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);
la solution proposée concerne trois listes, mais elle peut aussi s'appliquer à deux listes. En Java 8, nous pouvons utiliser le flux .de ou .concat :
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 comme entrée et crée un flux paresseusement concaténé dont les éléments sont tous les éléments du premier flux suivi de tous les éléments du second flux. Comme nous l'avons trois listes que nous avons utilisé cette méthode ( Stream.concat
) deux fois.
nous pouvons aussi écrire une classe d'utilité avec une méthode qui prend n'importe quel nombre de listes (en utilisant varargs ) et retourne une liste concaténée comme:
public static <T> List<T> concatenatedList(List<T>... collections) {
return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList());
}
alors nous pouvons faire usage de cette méthode comme:
List<String> result3 = StringUtils.concatenatedList(list1,list2,list3);
vous pouvez faire un oneliner si la liste cible est prédéclarée.
(newList = new ArrayList<String>(list1)).addAll(list2);
une autre solution de gaine utilisant Java8
stream, 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 (l'autre):
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;
}
version Java 8 avec prise en charge de l'adhésion 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());
}
vous pourriez le faire avec une importation statique et une classe helper
nb la généralisation 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;
}
}
alors vous pouvez faire des choses comme
import static Lists.join;
List<T> result = join(list1, list2, list3, list4);
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;
}
utilisez une classe Helper.
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)));
}
, Vous pouvez créer votre générique Java 8 utilitaire méthode concat un certain nombre de listes .
@SafeVarargs
public static <T> List<T> concat(List<T>... lists) {
return Stream.of(lists).flatMap(List::stream).collect(Collectors.toList());
}
Je ne prétends pas que c'est simple, mais vous avez mentionné bonus pour les monoliers; -)
Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
new Vector(list1).elements(),
new Vector(list2).elements(),
...
}))
il N'y a rien près d'une doublure, 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 en utilisant streams et java 8 si vos listes ont des types différents et que vous voulez 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 faire ceci statiquement vous pouvez le suivant.
les exemples utilisent 2 Énumérets dans l'ordre naturel (==enum-ordre) A, B
et rejoint alors 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);
}}
);
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;
}
public class TestApp {
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Hi");
Set<List<String>> bcOwnersList = new HashSet<List<String>>();
List<String> bclist = new ArrayList<String>();
List<String> bclist1 = new ArrayList<String>();
List<String> object = new ArrayList<String>();
object.add("BC11");
object.add("C2");
bclist.add("BC1");
bclist.add("BC2");
bclist.add("BC3");
bclist.add("BC4");
bclist.add("BC5");
bcOwnersList.add(bclist);
bcOwnersList.add(object);
bclist1.add("BC11");
bclist1.add("BC21");
bclist1.add("BC31");
bclist1.add("BC4");
bclist1.add("BC5");
List<String> listList= new ArrayList<String>();
for(List<String> ll : bcOwnersList){
listList = (List<String>) CollectionUtils.union(listList,CollectionUtils.intersection(ll, bclist1));
}
/*for(List<String> lists : listList){
test = (List<String>) CollectionUtils.union(test, listList);
}*/
for(Object l : listList){
System.out.println(l.toString());
}
System.out.println(bclist.contains("BC"));
}
}
Je ne peux pas améliorer le double-liner dans le cas général sans introduire votre propre méthode d'utilité, mais si vous avez des listes de chaînes et vous êtes prêt à supposer que ces chaînes ne contiennent pas de virgule, Vous pouvez tirer ce long one-liner:
List<String> newList = new ArrayList<String>(Arrays.asList((listOne.toString().subString(1, listOne.length() - 1) + ", " + listTwo.toString().subString(1, listTwo.length() - 1)).split(", ")));
si vous abandonnez les génériques, cela devrait être JDK conforme 1.4 (bien que je n'ai pas testé cela). Également déconseillé pour le code de production; -)