Como faço para juntar duas listas em Java?

Condições: não modifique as listas originais; Apenas JDK, sem bibliotecas externas. Pontos de bônus para um one-liner ou uma versão JDK 1.3.

Existe uma maneira mais simples que:

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

Fora do topo da minha cabeça, eu posso encurtá-lo por uma linha:

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

No Java 8:

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

Você poderia usar a biblioteca Apache commons-collections :

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

Provavelmente não mais simples, mas intrigante e feio:

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

Não use no código de produção …;)

Um dos seus requisitos é preservar as listas originais. Se você criar uma nova lista e usar addAll() , estará efetivamente duplicando o número de referências aos objects em suas listas. Isso pode levar a problemas de memory se as suas listas forem muito grandes.

Se você não precisar modificar o resultado concatenado, poderá evitar isso usando uma implementação de lista personalizada. A class de implementação personalizada é mais do que uma linha, obviamente … mas usá-la é curta e doce.

CompositeUnmodifiableList.java:

 public class CompositeUnmodifiableList extends AbstractList { private final List list1; private final List list2; public CompositeUnmodifiableList(List list1, List 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 newList = new CompositeUnmodifiableList(listOne,listTwo); 

Não é mais simples, mas sem resize a sobrecarga:

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

Encontrei esta questão procurando concatenar uma quantidade arbitrária de listas, não importando bibliotecas externas. Então, talvez ajude alguém:

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

Útil se você quiser aplicar a mesma lógica a um número de collections diferentes em um para ().

Outro one-liner do Java 8:

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

Como bônus, como o Stream.of() é variado, você pode concatenar quantas listas desejar.

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

Aqui está uma solução do java 8 usando duas linhas:

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

Esteja ciente de que este método não deve ser usado se

  • a origem de newList não é conhecida e pode já ser compartilhada com outros tópicos
  • o stream que modifica newList é um stream paralelo e o access a newList não está sincronizado nem é newListnewList

devido a efeitos colaterais.

Ambas as condições acima não se aplicam ao caso acima de unir duas listas, portanto, isso é seguro.

Com base nessa resposta para outra pergunta.

Isso é simples e apenas uma linha, mas adicionará o conteúdo de listTwo a listOne. Você realmente precisa colocar o conteúdo em uma terceira lista?

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

Ligeiramente mais simples:

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

Um pouco mais curto seria:

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

A solução proposta é para três listas, embora também possa ser aplicada a duas listas. No Java 8, podemos usar o Stream.of ou o Stream.concat como:

 List result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList()); List result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList()); 

Stream.concat usa dois streams como input e cria um stream concatenado preguiçosamente cujos elementos são todos os elementos do primeiro stream seguidos por todos os elementos do segundo stream. Como temos três listas, usamos este método ( Stream.concat ) duas vezes.

Também podemos escrever uma class de utilitário com um método que usa qualquer número de listas (usando varargs ) e retorna uma lista concatenada como:

 public static  List concatenatedList(List... collections) { return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList()); } 

Então podemos fazer uso deste método como:

 List result3 = StringUtils.concatenatedList(list1,list2,list3); 

Você pode fazer um oneliner se a lista de alvos for pré-declarada.

 (newList = new ArrayList(list1)).addAll(list2); 

No Java 8 (o outro caminho):

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

outra solução de um liner usando o stream Java8 , já que a solução flatMap já está postada, aqui está uma solução sem flatMap

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

ou

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

código

  List> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)); List li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll); System.out.println(lol); System.out.println(li); 

saída

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

O mais inteligente na minha opinião:

 /** * @param smallLists * @return one big list containing all elements of the small ones, in the same order. */ public static  List concatenate (final List ... smallLists) { final ArrayList bigList = new ArrayList(); for (final List list: smallLists) { bigList.addAll(list); } return bigList; } 

Versão do Java 8 com suporte para unir por chave de object:

 public List mergeLists(final List left, final List right, String primaryKey) { final Map mergedList = new LinkedHashMap<>(); Stream.concat(left.stream(), right.stream()) .map(someObject -> new Pair(someObject.getSomeKey(), someObject)) .forEach(pair-> mergedList.put(pair.getKey(), pair.getValue())); return new ArrayList<>(mergedList.values()); } 

Você poderia fazer isso com uma importação estática e uma class auxiliar

nb a generificação desta class provavelmente poderia ser melhorada

 public class Lists { private Lists() { } // can't be instantiated public static List join(List... lists) { List result = new ArrayList(); for(List list : lists) { result.addAll(list); } return results; } } 

Então você pode fazer coisas como

 import static Lists.join; List result = join(list1, list2, list3, list4); 
 public static  List merge(List... args) { final List result = new ArrayList<>(); for (List list : args) { result.addAll(list); } return result; } 

Use uma class auxiliar.

Eu sugiro:

 public static  Collection addAll(Collection 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(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c"))); // does not compile // System.out.println(addAll(new ArrayList(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c"))); System.out.println(addAll(new ArrayList(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6))); } 

Você pode criar seu método utilitário genérico do Java 8 para concatenar qualquer número de listas .

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

Eu não estou afirmando que é simples, mas você mencionou bônus para one-liners 😉

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

De jeito nenhum perto de uma linha, mas acho que é o mais simples:

 List newList = new ArrayList(l1); newList.addAll(l2); for(String w:newList) System.out.printf("%s ", w); 

Aqui está uma abordagem usando streams e java 8 se suas listas tiverem tipos diferentes e você quiser combiná-los a uma lista de outro tipo.

 public static void main(String[] args) { List list2 = new ArrayList<>(); List> 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> res = (List>) stream .map(item -> { if (item instanceof String) { return new Pair<>(0, item); } else { return new Pair<>(((Pair)item).getKey(), ((Pair)item).getValue()); } }) .collect(Collectors.toList()); } 

Se você quiser fazer isso estaticamente, você pode o seguinte.

Os exemplos usam 2 EnumSets em ordem natural (== Enum-order) A, B e junções em uma lista ALL .

 public static final EnumSet CATEGORY_A = EnumSet.of(A_1, A_2); public static final EnumSet CATEGORY_B = EnumSet.of(B_1, B_2, B_3); public static final List ALL = Collections.unmodifiableList( new ArrayList(CATEGORY_A.size() + CATEGORY_B.size()) {{ addAll(CATEGORY_A); addAll(CATEGORY_B); }} ); 
 public static  List merge(@Nonnull final List... list) { // calculate length first int mergedLength = 0; for (List ts : list) { mergedLength += ts.size(); } final List mergedList = new ArrayList<>(mergedLength); for (List ts : list) { mergedList.addAll(ts); } return mergedList; } 
 List< ?> newList = ListUtils.combine(list1, list2); 

Você tem que implementar o método de combine 🙂

 public class TestApp { /** * @param args */ public static void main(String[] args) { System.out.println("Hi"); Set> bcOwnersList = new HashSet>(); List bclist = new ArrayList(); List bclist1 = new ArrayList(); List object = new ArrayList(); 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 listList= new ArrayList(); for(List ll : bcOwnersList){ listList = (List) CollectionUtils.union(listList,CollectionUtils.intersection(ll, bclist1)); } /*for(List lists : listList){ test = (List) CollectionUtils.union(test, listList); }*/ for(Object l : listList){ System.out.println(l.toString()); } System.out.println(bclist.contains("BC")); } } 

Eu não posso melhorar o two-liner no caso geral sem introduzir seu próprio método utilitário, mas se você tiver listas de Strings e você estiver disposto a assumir que as Strings não contenham vírgulas, você pode extrair este -forro:

 List newList = new ArrayList(Arrays.asList((listOne.toString().subString(1, listOne.length() - 1) + ", " + listTwo.toString().subString(1, listTwo.length() - 1)).split(", "))); 

Se você eliminar os genéricos, isso deve ser compatível com o JDK 1.4 (embora eu não tenha testado isso). Também não é recomendado para o código de produção 😉