Como ingresso em duas listas em Java?

749

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

Existe uma maneira mais simples do que:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
Robert Atkins
fonte
5
Se você está fazendo isso apenas para iteração fins ver outra pergunta - há google goiaba e java 8 soluções stackoverflow.com/questions/4896662/...
Boris Treukhov
Solução Java 8 com o método utilitário: stackoverflow.com/a/37386846/1216775
akhil_mittal 29/04
Depois de ler algumas das respostas, desculpe-me por perguntar.
Anthony Rutledge

Respostas:

591

No Java 8:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());
Dale Emery
fonte
82
Gawd, isso é uma coisa no Java 8? Tecnicamente, você ganhar, eu acho, mas isso é um pedaço de uma longa linha :-)
Robert Atkins
4
Para o leitor casual, aqui é uma solução de curto usando também Java _ Streams: stackoverflow.com/a/34090554/363573
Stephan
7
É feio, mas pelo menos é fluente e pode ser usado sem lambdas de várias linhas. Eu realmente gostaria que houvesse um addAll fluente que retornasse a lista concatinada.
precisa saber é o seguinte
6
Eu acho que vale a pena notar que também é muito fácil obter uma lista distinta disso, assim:List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
302 Roger Roger
1
Alternativa para concat: corrente de fluxosStream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser
569

Em cima da minha cabeça, posso encurtá-lo em uma linha:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
AdamC
fonte
156
Enquanto você está tecnicamente correto, você o reduziu em uma linha, a assimetria disso me incomoda. O suficiente para ficar mais feliz em "gastar" a linha extra.
Robert Atkins
13
Não há um problema aqui em que a matriz interal da newList será inicializada com o tamanho de listOne e precisará potencialmente expandir ao adicionar todos os itens de listTwo? Seria melhor pegar o tamanho de cada lista e usá-lo para dimensionar a nova matriz?
Eric
2
Esta foi a solução que funcionou melhor para mim. Fiz uma comparação sobre o desempenho de diferentes soluções que resultaram vencedor, juntamente com a criação da lista vazia e depois addAll()de ambas. Tentei todos aqueles que sugerem não copiar as listas e eles resultam em muita sobrecarga que não precisávamos dessa vez.
Manuelvigarcia # 26/16
legal, mas você pode até torná-lo mais curto: List list = new ArrayList <> (list1) .addAll (list2);
velocidade de
1
@velocity: não, isso não vai funcionar. addAll(Collection)retorna a boolean.
Stijn Van Bael
306

Você pode usar a biblioteca de coleções comuns do Apache :

List<String> newList = ListUtils.union(list1, list2);
Guillermo
fonte
52
Bom, mas requer apache commons. Ele especificou 'nenhuma biblioteca externa'
Quantum7
101
@ Quantum7, ainda útil para outras pessoas;) Além disso, o apache commons é mesmo uma biblioteca externa? Eu não começo nada sem ele!
tster 30/03/12
28
@Platinum Não, de acordo com os documentos ListUtils.union é exatamente equivalente ao código do OP. Mas talvez seja enganoso usar uma operação SET ("Union") em um contexto de lista. Eu posso ver como você pode esperar que isso remova duplicatas ou algo assim, mas parece que o método não faz isso.
Quantum7
24
Evite coleções do Apache Commons. Não é seguro, não há genéricos. Ótimo se você usa o Java 1.4, mas para o Java 5 e superior, eu prefiro o Google Guava.
Michael Piefel
11
@MichaelPiefel As últimas coleções 4 do Apache Commons são de tipo seguro. Com a referência do método Java 8, esse tipo de utilitário estático se torna muito importante.
precisa saber é
93

Um dos seus requisitos é preservar as listas originais. Se você criar uma nova lista e usar addAll(), estará dobrando efetivamente o número de referências aos objetos em suas listas. Isso pode causar problemas de memória se 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 classe de implementação personalizada é mais de uma linha, obviamente ... mas usá-la é curta e agradável.

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

Uso:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
Kevin K
fonte
15
Esta é a verdadeira resposta para esta pergunta.
Wouter Lievens
9
Esta é uma solução viável, mas observe que, se os objetos da lista subjacente mudarem (lista1, lista2), o conteúdo dessa lista será alterado. Talvez você não consiga modificar uma instância da própria CompositeUnmodifiableList , mas se conseguir obter uma referência às listas originais, poderá. Também para aqueles que não estão familiarizados: o modificador final apenas afeta a referência ao objeto da lista em si não pode ser alterado, mas ainda é possível que o conteúdo da lista seja alterado!
JWJ
3
@ jwj todos os pontos muito bons, obrigado. O nome da classe provavelmente merece alguma explicação. Eu vejo essa classe fazendo algo muito semelhante ao Collections.unmodifiableList()método, que envolve uma lista para torná-la inalterável. CompositeUnmodifiableListfaz a mesma coisa, exceto que envolve duas listas e fornece uma exibição concatenada. Todos os pontos que você menciona CompositeUnmodifiableListtambém são verdadeiros Collections.unmodifiableList().
Kevin K
2
O construtor pode levarList<? extends E>
Patrick Parker
84

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

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

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

vôlei
fonte
44
Feio e mau, assim como quase qualquer uso da inicialização com chaves duplas. É mais curto, embora;)
Jorn
4
@MarnixKlooster: Eclipse sabe que você não deve usá-lo e torna desagradável para usar ;-)
Joachim Sauer
20
Embora seja fisicamente uma linha, não considero isso uma "linha única".
splungebob
11
por que as pessoas odeiam initializers bloco anônimo
Nim Chimpsky
18
@NimChimpsky Eu acho que é principalmente porque não é apenas um inicializador de bloco anônimo, mas você está criando uma subclasse anônima de ArrayList. Dito isto, se você confia nos resultados desta pergunta de inicialização com cinta dupla , parece que odiar o DBI é principalmente uma questão de gosto estilístico e micro-otimização. Até onde eu sei, não há grandes penalidades por fazê-lo. A desvantagem sorrateira seria se você tentasse comparar sua classe porque não será ArrayList.
27412 Patrick
75

Outro linux de Java 8:

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

Como bônus, como Stream.of()é variável, você pode concatenar quantas listas quiser.

List<String> newList = Stream.of(listOne, listTwo, listThree)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());
Marca
fonte
35
x -> x.stream()pode ser substituído por Collection::stream.
Martin
10
... ou mesmo com List::stream.
MC Emperor
73

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

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

Encontrei essa pergunta procurando concatenar quantidades arbitrárias de listas, sem importar as bibliotecas externas. Então, talvez ajude alguém:

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

Útil se você deseja aplicar a mesma lógica a várias coleções diferentes em uma para ().

Yuri Geinish
fonte
9
Por exemplo: Lists.newArrayList (Iterables.concat (list1, list2));
21714
você deveria ligar em com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)vez de Iterables#concat(); porque o posterior ainda copia elementos no link temporário!
bob
45

Java 8 ( Stream.ofe Stream.concat)

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

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.concatrecebe dois fluxos como entrada e cria um fluxo concatenado preguiçosamente cujos elementos são todos os elementos do primeiro fluxo seguidos por todos os elementos do segundo fluxo. Como temos três listas, usamos esse método ( Stream.concat) duas vezes.

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

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

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

List<String> result3 = Utils.concatenateLists(list1,list2,list3);
akhil_mittal
fonte
Você provavelmente está disposto a dizer List <> resultado1 = Stream.concat (Stream.concat (list1.stream (), list2.stream ()), list3.stream ()). Collect (Collectors.toList ()); no seu primeiro operador. Por favor, conserte.
WebComer
44

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

List<Object> 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 newListnão é conhecida e já pode ser compartilhada com outros threads
  • o fluxo que modifica newListé um fluxo paralelo e o acesso ao newListnão é sincronizado ou seguro por thread

devido a considerações sobre efeitos colaterais.

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

Com base nesta resposta a outra pergunta.

SpaceTrucker
fonte
12
Se não estou errado, isso realmente não é recomendado - docs.oracle.com/javase/8/docs/api/java/util/stream/… Consulte a seção de efeitos colaterais. > Os efeitos colaterais nos parâmetros comportamentais das operações de fluxo são, em geral, desencorajados, pois muitas vezes podem levar a violações involuntárias do requisito de apatridia, além de outros riscos à segurança do thread. Portanto, nesse caso, é melhor usar Collectors.toList ()
Anton Balaniuc 14/17
@AntonBalaniuc A questão é se esse é realmente um efeito colateral. Nesse ponto, newListnão é observável por nenhum outro segmento. Mas você está correto que isso provavelmente não deve ser feito se não se sabe onde o valor do newListveio (por exemplo, se newListfoi passado como um parâmetro.
SpaceTrucker
2
Estou curioso; por que ao .forEach(newList::addAll);invés de .collect(Collectors.toList());?
11684
4
@ 11684 porque o coletor coletaria a List<List<Object>>. O que você pode ter em mente é algo como isto: stackoverflow.com/questions/189559/…
SpaceTrucker
@ SpaceTrucker Opa, eu ignorei isso. Obrigado por esclarecer minha confusão. Sim, eu deveria estar pensando flatMap.
11684
34

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

Collections.addAll(listOne, listTwo.toArray());
ceklock
fonte
11
Não modificar as listas originais foi um dos critérios, mas é útil ter aqui como exemplo para situações em que isso não é uma restrição.
Robert Atkins
1
Obrigado, ou ainda mais simples, listOne.addAll (listTwo)
Jay
27

Um pouco mais simples:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Tim
fonte
Isso causaria seqüências de caracteres duplicadas? Significando que uma String que existe nas duas listas existirá duas vezes na lista resultante?
AgentKnopf
4
@ Zainodis Sim, pode haver duplicatas. A Listestrutura não impõe restrições de exclusividade. Você pode remover dupes fazendo o mesmo com conjuntos. Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
27412 Patrick
20

Um pouco mais curto seria:

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

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

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

Você pode executar um oneliner se a lista de destinos for pré-declarada.

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

No Java 8 (o contrário):

List<?> newList = 
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
Nitin Jain
fonte
Essa abordagem já é sugerida por esta resposta: stackoverflow.com/a/37386846
Miles
9

outra solução de liner usando Java8stream, como a flatMapsolução já foi lançada, aqui está uma solução semflatMap

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

código

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

resultado

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
Saravana
fonte
1
Gostaria de acrescentar que esta solução é provavelmente mais eficaz do que o uso flatMap, porque as listas são apenas reiterou uma vez quando são recolhidos
Stefan Haberl
7

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 <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;
}
Olivier Faucheux
fonte
3
Não esqueça o @SafeVarargs!
Radon Rosborough
6

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

A geração desta classe provavelmente poderia ser melhorada.

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

}

Então você pode fazer coisas como

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);
Dave Cheney
fonte
Como a importação estática ou a classe auxiliar é relevante?
Shmosel
6

Versão Java 8 com suporte para união por chave de objeto:

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());
}
cslysy
fonte
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;
}
martyglaubitz
fonte
4

Use uma classe Helper.

Eu sugiro:

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)));
}
alex
fonte
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;
  }
Langusten Gustel
fonte
2

Podemos juntar 2 listas usando java8 com 2 abordagens.

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

1) Usando concat:

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

2) Usando flatMap:

    List<String> collect3 = Stream.of(list1, list2).flatMap(Collection::stream).collect(toList());
    System.out.println("collect3 = " + collect3); // collect3 = [S, T, U, V]
Himank Batra
fonte
1
Ao responder a uma pergunta de onze anos com outras trinta respostas, lembre-se de apontar quais novos aspectos da pergunta sua resposta aborda e observe se essas técnicas teriam funcionado quando a pergunta foi feita ou se dependem de recursos que foram introduzido ao longo dos anos.
Jason Aller
2

Quase todas as respostas sugerem o uso de um ArrayList.

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

Prefira usar um LinkedList para operações eficientes de adição.

A adição de ArrayList é O (1) amortizada, mas O (n) na pior das hipóteses, pois a matriz deve ser redimensionada e copiada. Enquanto o LinkedList add é sempre constante O (1).

mais informações https://stackoverflow.com/a/322742/311420

Raymond Chenon
fonte
0

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(),
    ...
}))
ddimitrov
fonte
por que alguém nunca deveria usá-los?
David
5
@ David porque pretendia ser usado internamente no JDK. Se você usou isso no seu código, provavelmente o código não será executado no JDK / JRE que não é da Sun (ou não é da Oracle agora).
Adrian Shum
@AdrianShum Existem outros JDKs / JREs que não o Oracle? Isso me surpreenderia. Mesmo se limitado à funcionalidade API mais comum, reconstruindo esse material todo provavelmente levaria idades ...
Egor Hans
1
Existe bastante JVM. Mais um comumente visto no mundo empresarial deve ser o IBM aquele que é, IIRC, empacotado com websphere
Adrian Shum
0

De maneira nenhuma perto de uma linha, mas acho que é o mais simples:

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

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

Aqui está uma abordagem usando fluxos e java 8 se suas listas tiverem tipos diferentes e você desejar combiná-las com uma lista de outro tipo.

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

Se você quiser fazer isso estaticamente, faça o seguinte.

Os exemplos usam 2 EnumSets em ordem natural (== Enum-order) A, Be se juntam a uma ALLlista.

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);
                  }}
              );
Jan Weitz
fonte
Isso criaria uma nova classe anônima. Abordagem não recomendada!
Kravemir
-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;
    }

}
benez
fonte