Como copiar a lista de collections Java

Eu tenho um ArrayList e quero copiá-lo exatamente. Eu uso classs de utilidade quando possível, supondo que alguém passou algum tempo fazendo isso correto. Então, naturalmente, acabo com a class Collections que contém um método de cópia.

Suponha que eu tenha o seguinte:

 List a = new ArrayList(); a.add("a"); a.add("b"); a.add("c"); List b = new ArrayList(a.size()); Collections.copy(b,a); 

Isso falha porque basicamente acha que b não é grande o suficiente para conter a . Sim, eu sei que b tem tamanho 0, mas deve ser grande o suficiente agora, não deveria? Se eu tiver que preencher b primeiro, então Collections.copy() se torna uma function completamente inútil em minha mente. Então, exceto por programar uma function de cópia (o que eu vou fazer agora), existe uma maneira correta de fazer isso?

Chamando

 List b = new ArrayList(a); 

cria uma cópia superficial de a dentro de b . Todos os elementos existirão dentro de b na mesma ordem em que estavam dentro de a (supondo que tivesse um pedido).

Da mesma forma, chamando

 // note: instantiating with a.size() gives `b` enough capacity to hold everything List b = new ArrayList(a.size()); Collections.copy(b, a); 

também cria uma cópia superficial de a dentro de b . Se o primeiro parâmetro, b , não tiver capacidade suficiente (não tamanho) para conter todos os elementos de um, ele lançará um IndexOutOfBoundsException . A expectativa é que nenhuma alocação seja necessária para o Collections.copy funcionar e, se houver, a exceção será lançada. É uma otimização para exigir que a coleção copiada seja pré-alocada ( b ), mas geralmente não acho que o recurso valha a pena devido às verificações necessárias, dadas as alternativas baseadas em construtor como a mostrada acima, que não têm efeitos colaterais estranhos.

Para criar uma cópia profunda, a List , por meio de qualquer dos mecanismos, teria que ter conhecimento intrincado do tipo subjacente. No caso de String s, que são imutáveis ​​em Java (e .NET), você nem precisa de uma cópia profunda. No caso do MySpecialObject , você precisa saber como fazer uma cópia profunda dele e que não é uma operação genérica.


Observação: a resposta originalmente aceita foi o principal resultado do Collections.copy no Google, e foi totalmente errado, conforme apontado nos comentários.

b tem uma capacidade de 3, mas um tamanho de 0. O fato de ArrayList ter algum tipo de capacidade de buffer é um detalhe de implementação – não faz parte da interface List , então Collections.copy(List, List) não o usa . Seria feio para a ArrayList casos especiais.

Como MrWiggles indicou, usar o construtor ArrayList que leva uma coleção é o caminho para o exemplo fornecido.

Para cenários mais complicados (que podem include seu código real), você pode achar a biblioteca de Coleções Java do Google útil.

Apenas faça:

 List a = new ArrayList(); a.add("a"); a.add("b"); a.add("c"); List b = new ArrayList(a); 

ArrayList tem um construtor que aceitará outra coleção para copiar os elementos de

A resposta de Stephen Katulka (resposta aceita) está errada (a segunda parte). Explica que Collections.copy(b, a); faz uma cópia profunda, o que não acontece. Ambos, new ArrayList(a); e Collections.copy(b, a); faça apenas uma cópia superficial. A diferença é que o construtor aloca nova memory, e copy(...) não, o que a torna adequada nos casos em que você pode reutilizar matrizes, já que tem uma vantagem de desempenho.

A API padrão Java tenta desencorajar o uso de cópias profundas, já que seria ruim se novos codificadores usassem isso regularmente, o que também pode ser uma das razões pelas quais o clone() não é público por padrão.

O código fonte para Collections.copy(...) pode ser visto na linha 552 em: http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip- Registrando-regex / java / util / Collections.java.htm

Se você precisar de uma cópia profunda, precisará iterar os itens manualmente, usando um loop for e clone () em cada object.

A maneira mais simples de copiar uma lista é passá-la para o construtor da nova lista:

 List b = new ArrayList<>(a); 

b será uma cópia superficial de a

Olhando para a fonte de Collections.copy(List,List) (eu nunca vi isso antes) parece ser para lidar com o índice de elementos por índice. usando List.set(int,E) assim, o elemento 0 irá List.set(int,E) elemento 0 na lista de alvos, etc. etc. Não é particularmente claro a partir dos javadocs que eu tenho que admitir.

 List a = new ArrayList<>(a); a.add("foo"); b.add("bar"); List b = new ArrayList<>(a); // shallow copy 'a' // the following will all hold assert a.get(0) == b.get(0); assert a.get(1) == b.get(1); assert a.equals(b); assert a != b; // 'a' is not the same object as 'b' 
 List b = new ArrayList(a.size()) 

não define o tamanho. Ele define a capacidade inicial (sendo quantos elementos ele pode ajustar antes de precisar resize). Uma maneira mais simples de copiar, neste caso, é:

 List b = new ArrayList(a); 

Como hoijui menciona. A resposta selecionada de Stephen Katulka contém um comentário sobre Collections.copy que está incorreto. O autor provavelmente aceitou porque a primeira linha de código estava fazendo a cópia que ele queria. A chamada adicional para Collections.copy apenas copia novamente. (Resultando na cópia acontecendo duas vezes).

Aqui está o código para provar isso.

 public static void main(String[] args) { List a = new ArrayList(); a.add("a"); a.add("b"); a.add("c"); List b = new ArrayList(a); System.out.println("There should be no output after this line."); // Note, b is already a shallow copy of a; for (int i = 0; i < a.size(); i++) { if (a.get(i) != b.get(i)) { System.out.println("Oops, this was a deep copy."); // Note this is never called. } } // Now use Collections.copy and note that b is still just a shallow copy of a Collections.copy(b, a); for (int i = 0; i < a.size(); i++) { if (a.get(i) != b.get(i)) { System.out.println("Oops, i was wrong this was a deep copy"); // Note this is never called. } } // Now do a deep copy - requires you to explicitly copy each element for (int i = 0; i < a.size(); i++) { b.set(i, new String(a.get(i))); } // Now see that the elements are different in each for (int i = 0; i < a.size(); i++) { if (a.get(i) == b.get(i)) { System.out.println("oops, i was wrong, a shallow copy was done."); // note this is never called. } } } 

A maioria das respostas aqui não percebe o problema, o usuário quer ter uma cópia dos elementos da primeira lista para a segunda lista, os elementos da lista de destino são novos objects e não referência aos elementos da lista original. (significa que alterar um elemento da segunda lista não deve alterar valores para o elemento correspondente da lista de origem.) Para os objects mutáveis, não podemos usar o construtor ArrayList (Collection) porque ele simplesmente fará referência ao elemento da lista original e não copiará. Você precisa ter um clonador de lista para cada object ao copiar.

Por que você não usa apenas o método addAll :

  List a = new ArrayList(); a.add("1"); a.add("abc"); List b = b.addAll(listA); //b will be 1, abc 

mesmo se você tiver itens existentes em b ou quiser pendurar alguns elementos depois disso, como:

 List a = new ArrayList(); a.add("1"); a.add("abc"); List b = new ArrayList(); b.add("x"); b.addAll(listA); b.add("Y"); //b will be x, 1, abc, Y 

Se você deseja copiar uma ArrayList, copie-a usando:

 List b = new ArrayList(); b.add("aa"); b.add("bb"); List a = new ArrayList(b); 

As cordas podem ser copiadas com profundidade

 List b = new ArrayList(a); 

porque eles são imutáveis. Qualquer outro Objeto não -> você precisa iterar e fazer uma cópia sozinho.

 private List cloneItemList(final List items) { Item[] itemArray = new Item[items.size()]; itemArray = items.toArray(itemArray); return Arrays.asList(itemArray); } 

Qualquer outro Objeto não -> você precisa iterar e fazer uma cópia sozinho.

Para evitar isso, implante Cloneable.

 public class User implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private String user; private String password; ... @Override public Object clone() { Object o = null; try { o = super.clone(); } catch(CloneNotSupportedException e) { } return o; } } 

….

  public static void main(String[] args) { List userList1 = new ArrayList(); User user1 = new User(); user1.setUser("User1"); user1.setPassword("pass1"); ... User user2 = new User(); user2.setUser("User2"); user2.setPassword("pass2"); ... userList1 .add(user1); userList1 .add(user2); List userList2 = new ArrayList(); for(User u: userList1){ u.add((User)u.clone()); } //With this you can avoid /* for(User u: userList1){ User tmp = new User(); tmp.setUser(u.getUser); tmp.setPassword(u.getPassword); ... u.add(tmp); } */ } 

A saída a seguir ilustra os resultados do uso do construtor de cópias e Collections.copy ():

 Copy [1, 2, 3] to [1, 2, 3] using copy constructor. Copy [1, 2, 3] to (smaller) [4, 5] java.lang.IndexOutOfBoundsException: Source does not fit in dest at java.util.Collections.copy(Collections.java:556) at com.farenda.java.CollectionsCopy.copySourceToSmallerDest(CollectionsCopy.java:36) at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:14) Copy [1, 2] to (same size) [3, 4] source: [1, 2] destination: [1, 2] Copy [1, 2] to (bigger) [3, 4, 5] source: [1, 2] destination: [1, 2, 5] Copy [1, 2] to (unmodifiable) [4, 5] java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableList.set(Collections.java:1311) at java.util.Collections.copy(Collections.java:561) at com.farenda.java.CollectionsCopy.copyToUnmodifiableDest(CollectionsCopy.java:68) at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:20) 

A fonte do programa completo está aqui: Cópia da Lista Java . Mas a saída é suficiente para ver como o java.util.Collections.copy () se comporta.

E se você estiver usando google goava, a solução de uma linha seria

 List b = Lists.newArrayList(a); 

Isso cria uma instância de lista de matriz mutável.

Copiar não é inútil se você imaginar o caso de uso para copiar alguns valores em uma coleção existente. Ou seja, você deseja replace elementos existentes em vez de inserir.

Um exemplo: a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4,] a.copy (b) = [1,2,3,4,5,3,3,3,3,4,4,4]

No entanto, eu esperaria um método de cópia que usasse parâmetros adicionais para o índice inicial da coleção de origem e de destino, bem como um parâmetro para contagem.

Veja Java BUG 6350752

Com o Java 8 sendo nulo, você pode usar o seguinte código.

 List b = Optional.ofNullable(a) .map(list -> (List) new ArrayList<>(list)) .orElseGet(Collections::emptyList); 

Ou usando um colecionador

 List b = Optional.ofNullable(a) .map(List::stream) .orElseGet(Stream::empty) .collect(Collectors.toList()) 

Para entender por que Collections.copy () lança uma IndexOutOfBoundsException, embora você tenha feito a matriz de back-up da lista de destinos grande o suficiente (através da chamada size () na sourceList), veja a resposta de Abhay Yadav nesta questão relacionada: Copie um java.util.List em outro java.util.List