Como clonar uma lista genérica em Java?

155

Eu tenho um ArrayList<String>que gostaria de devolver uma cópia. ArrayListpossui um método clone que possui a seguinte assinatura:

public Object clone()

Depois de chamar esse método, como converter o objeto retornado de volta ArrayList<String>?

Bill the Lizard
fonte
16
Não, esta é uma pergunta válida. O Java não suporta genéricos "verdadeiros", com apagamento do tipo de tempo de execução e tudo, portanto, esses tipos de detalhes podem ser complicados. Além disso, a interface Cloneable e o mecanismo do método Object.clone () são igualmente confusos.
Outlaw Programmer
OK, eu principalmente faço C # onde isso é realmente fácil. Informe-me se você deseja remover os comentários desta pergunta.
Espo
1
Você pode deixar o comentário Acho que minhas edições explicaram o que eu estava tendo problemas.
Bill o Lagarto
2
Seu comentário é bom, se um pouco condescendente. Eu imagino que muitos desenvolvedores que os desenvolvedores de Java precisam percorrer parecem bobos para os desenvolvedores do .NET.
Outlaw Programmer
1
@ Oscar, ele quer clonar e não invocar o comando clone. Pode não ser o mesmo que o clone realmente não clona. Eu acho que esse é o ponto. Esta é realmente uma pergunta complicada.
Rafa

Respostas:

62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
Vinko Vrsalovic
fonte
43
Isso funcionará bem para Strings (que é o que a pergunta solicitada), mas vale a pena notar que ArrayList.clone executará uma cópia superficial, portanto, se houver objetos mutáveis ​​na lista, eles não serão clonados (e alterará um numa lista vai mudar isso um no outro lista também.
pkaeding
49
Você deve evitar o uso de tipos brutos em qualquer coisa, menos no código herdado. Você está melhor usando ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
Cdmckay #
20
É uma pena que o ArrayList tenha um método #clone, mas a própria lista não. Suspiro.
Rogerdpack # 24/12
12
Não relacionado, mas enquanto estiver no assunto: use em List<String>vez de ArrayList<String>no lado esquerdo. É assim que as coleções devem ser usadas na maioria das vezes.
Christophe Roussy
3
Não pude usar esse método para duplicar um ArrayList. O método clone () não foi reconhecido.
13133 Jack
318

Por que você gostaria de clonar? Criar uma nova lista geralmente faz mais sentido.

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

Tarefa concluída.

Tom Hawtin - linha de orientação
fonte
17
Você pode não saber que tipo de lista é essa. Pode ser um LinkedList, MyOwnCustomList ou uma subclasse de ArrayList; nesse caso, a novidade de um ArrayList seria do tipo incorreto.
Steve Kuo
51
Eu me importaria com qual implementação a lista original usou? Eu provavelmente me importo com qual implementação a nova lista usa.
Tom Hawtin - defina
12
@ Steve Kuo: A assinatura ArrayList(Collection<? extends E> c)significa que não importa que tipo de lista você usa como argumento.
Cdmckay 01/03/09
3
Quero dizer, não é uma cópia profunda, é?
4
@YekhezkelYovel Não, não seria.
Tom Hawtin - defina
19

Este é o código que eu uso para isso:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

A esperança é útil para você

Alemão
fonte
3
E evitar o uso de tipos de matérias .. então ao invés de ArrayListusoArrayList<YourObject>
milosmns
2
docs.oracle.com/javase/8/docs/api/java/util/… Não funciona porque os tamanhos da lista são diferentes.
MLProgrammer-CiM
Por que você não usaria apenas a sobrecarga de cópia do ArrayListconstrutor?
Tom Hawtin - tackline
19

Com o Java 8, ele pode ser clonado com um fluxo.

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());
Simon Jenkins
fonte
Pode ser feito como List <AnObject> xy = new ArrayList <> (oldList);
mirzak
1
Esta não é uma cópia profunda, alterações em elementos de uma lista pode ser visto em outro
Inchara
2
Onde na pergunta ele especifica que é necessária uma cópia em profundidade? A suposição é que outra coleção contendo os mesmos objetos é necessária. Se você deseja seguir o caminho da cópia profunda, está abrindo uma nova lata de worms.
22818 Simon Jenkins
Não é realmente um clone, não é? Nos documentos da API "Não há garantias sobre o tipo, mutabilidade, serialização ou segurança de encadeamento da lista retornada". Euw, não quero um desses. toCollectionpode ser uma escolha melhor.
Tom Hawtin # tackline
16

Esteja ciente de que Object.clone () tem alguns problemas importantes e seu uso é desencorajado na maioria dos casos. Consulte o Item 11, do " Java Efetivo ", de Joshua Bloch, para obter uma resposta completa. Eu acredito que você pode usar com segurança o Object.clone () em matrizes de tipo primitivo, mas, além disso, você precisa ser criterioso quanto ao uso e substituição do clone. Provavelmente, é melhor definir um construtor de cópias ou um método estático de fábrica que clone explicitamente o objeto de acordo com sua semântica.

Julien Chastang
fonte
15

Eu acho que isso deve fazer o truque usando a API de coleções:

Nota : o método de cópia é executado em tempo linear.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);
Aaron
fonte
13
Por que não usar apenas o novo ArrayList <> (oldList)?
Cdmckay #
4
Acredito que isso não funcionaria, do doc * A lista de destinos deve ter pelo menos o tamanho da lista de fontes. Se for mais longo, os elementos restantes na lista de destinos não serão afetados.
26412 Greg Domjan
@GregDomjan não que eu concorde que esta é a melhor maneira, mas é uma maneira de fazê-lo. Para contornar o problema, é tão simples quanto este: List <String> newList = new ArrayList <> (oldList.size ());
Nckbrz 18/06/2014
1
Não tenho certeza se está relacionado ao Java 8, mas mesmo ao especificar o tamanho, ainda obtendo uma exceção IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Parece que usar copyList.addAll(original);é uma boa alternativa
Gene Bo
@GeneBo Não está especificando o tamanho. Está especificando a capacidade, o que é uma coisa muito diferente. A alegria dos intargumentos misteriosos . Não sei por que você deseja usar esse método estático obscuro em vez de bom e velho addAll.
Tom Hawtin -
8

Acho que o uso de addAll funciona bem.

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

parênteses são usados ​​em vez da sintaxe genérica

Allain Lalonde
fonte
5
Isso funcionará para Strings, mas não para objetos mutáveis. Você também vai querer cloná-los.
jodonnell
Sim, a pergunta dele é para cordas. E ele tem o problema de genéricos que realmente não gostam do material de elenco nesse caso.
Allain Lalonde
Além disso, ArrayList.clone fará apenas um clone superficial, portanto os objetos mutáveis ​​da lista também não serão clonados usando esse método.
pkaeding 10/09/08
Isso deve ser ArrayList <>>. Além disso, você provavelmente está melhor usando o novo ArrayList <>> (original), pois é menos escrito e claro.
Cdmckay #
4
Por que não usar apenas new ArrayList<String>(original)?
user102008
6

Se você quiser isso para poder retornar a Lista em um getter, seria melhor fazer:

ImmutableList.copyOf(list);
Uri Shalit
fonte
4

Para clonar uma interface genérica como java.util.Listvocê só precisará convertê-la. aqui está um exemplo:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

É um pouco complicado, mas funciona, se você estiver limitado a retornar uma Listinterface, para que qualquer pessoa após você possa implementar sua lista sempre que ele quiser.

Sei que esta resposta está próxima da resposta final, mas minha resposta responde como fazer tudo isso enquanto você trabalha com List- o genérico - nãoArrayList

Ahmed Hamdy
fonte
2
você está assumindo que ele será sempre um ArrayList que não é verdade
jucardi
@JuanCarlosDiaz vai, esta é a pergunta feita, era sobre ArrayList, então eu respondi-lo para ArrayList :)
Ahmed Hamdy
2

Tenha muito cuidado ao clonar ArrayLists. A clonagem em java é superficial. Isso significa que ele apenas clonará a própria lista de arrays e não seus membros. Portanto, se você tiver um ArrayList X1 e cloná-lo no X2, qualquer alteração no X2 também se manifestará no X1 e vice-versa. Quando você clona, ​​você gera apenas uma nova ArrayList com ponteiros para os mesmos elementos no original.

Juan Besa
fonte
2

Isso também deve funcionar:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()
pkaeding
fonte
2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Lembre-se de que esta é apenas uma cópia superficial e não profunda, ou seja. você recebe uma nova lista, mas as entradas são as mesmas. Isso não é problema para simplesmente seqüências de caracteres. Fica mais complicado quando as entradas da lista são objetos.

Robert
fonte
1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();
jodonnell
fonte
1

Eu não sou um profissional de java, mas tenho o mesmo problema e tentei resolver por esse método. (Supõe que T tenha um construtor de cópias).

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}
Petr
fonte
Não há garantia de que a Listclasse de implementação tenha um construtor público no-arg. Por exemplo, o Lists retorna por Array.asList, List.ofou List.subListprovavelmente não.
Tom Hawtin -