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

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

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

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

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

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


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

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

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 ().


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

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.


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

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

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

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

13

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

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


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]

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

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

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

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

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

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

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]

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


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(),
    ...
}))

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

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

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

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

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.