¿Cómo clono una lista genérica en Java?

155

Tengo uno del ArrayList<String>que me gustaría devolver una copia. ArrayListtiene un método de clonación que tiene la siguiente firma:

public Object clone()

Después de llamar a este método, ¿cómo devuelvo el objeto devuelto ArrayList<String>?

Bill el lagarto
fuente
16
No, esta es una pregunta válida. Java no admite genéricos "verdaderos", con borrado de tiempo de ejecución y todo, por lo que este tipo de detalles puede ser complicado. Además, la interfaz clonable y el mecanismo del método Object.clone () es igualmente confuso.
Outlaw Programmer
OK, principalmente hago C # donde esto es realmente fácil. Avíseme si desea que elimine los comentarios de esta pregunta.
Espo
1
Puedes dejar el comentario. Creo que mis ediciones explicaron con qué estaba teniendo problemas.
Bill the Lizard
2
Su comentario está bien, aunque sea un poco condescendiente. Me imagino que muchos de los obstáculos que los desarrolladores de Java tienen que atravesar parecen tontos para los desarrolladores de .NET.
Outlaw Programmer
1
@Oscar, quiere clonar y no invocar el comando clonar. Puede que no sea lo mismo que el clon realmente no clona. Creo que este es el punto. Esta es de hecho una pregunta difícil.
Rafa

Respuestas:

62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
Vinko Vrsalovic
fuente
43
Esto funcionará bien para Strings (que es lo que pidió la pregunta), pero vale la pena señalar que ArrayList.clone realizará una copia superficial, por lo que si había objetos mutables en la lista, no se clonarán (y cambiarán uno en una lista también cambiará esa en la otra lista
enviando el
49
Debe evitar el uso de tipos sin formato en cualquier cosa que no sea código heredado. Es mejor usarlo ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay 01 de
20
Es una lástima que ArrayList tenga un método #clone, pero List en sí no. Suspiro.
rogerdpack
12
No relacionado, pero mientras lo hace: use en List<String>lugar de ArrayList<String>en el lado izquierdo. Así es como se deben usar las colecciones la mayor parte del tiempo.
Christophe Roussy
3
No pude usar este método para duplicar una ArrayList. El método clone () no fue reconocido.
Jack
318

¿Por qué querrías clonar? Crear una nueva lista generalmente tiene más sentido.

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

Trabajo hecho.

Tom Hawtin - tackline
fuente
17
Es posible que no sepa qué tipo de Lista es. Puede ser una LinkedList, MyOwnCustomList o una subclase de ArrayList, en cuyo caso la renovación de una ArrayList sería del tipo incorrecto.
Steve Kuo
51
¿Me importaría qué implementación utiliza la lista original? Probablemente me importe qué implementación usa la nueva lista.
Tom Hawtin - tackline
12
@ Steve Kuo: La firma ArrayList(Collection<? extends E> c)significa que no importa qué tipo de lista uses como argumento.
cdmckay 01 de
3
Quiero decir que no es una copia profunda, ¿verdad?
44
@YekhezkelYovel No, no lo sería.
Tom Hawtin - tackline
19

Este es el código que uso para eso:

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

La esperanza es útil para ti

Alemán
fuente
3
Y evite usar tipos sin procesar ... así que en lugar de ArrayListusarlosArrayList<YourObject>
milosmns
2
docs.oracle.com/javase/8/docs/api/java/util/… No funciona porque los tamaños de lista son diferentes.
MLProgrammer-CiM
¿Por qué no usarías la sobrecarga de copia del ArrayListconstructor?
Tom Hawtin - tackline
19

Con Java 8 se puede clonar con una secuencia.

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

...

List<AnObject> clone = myList.stream().collect(toList());
Simon Jenkins
fuente
Se puede hacer como List <AnObject> xy = new ArrayList <> (oldList);
mirzak
1
Esta no es una copia profunda, los cambios en los elementos de una lista se pueden ver en otra
Inchara
2
¿Dónde en la pregunta especifica que se requiere una copia profunda? La suposición es que se requiere otra colección que contenga los mismos objetos. Si desea seguir la ruta de la copia profunda, está abriendo una nueva lata de gusanos.
Simon Jenkins
Sin embargo, no es realmente un clon, ¿verdad? De los documentos de la API "No hay garantías sobre el tipo, mutabilidad, serialización o seguridad de subprocesos de la Lista devuelta". Euw, no quiero uno de esos. toCollectionPuede ser una mejor opción.
Tom Hawtin - tackline
16

Tenga en cuenta que Object.clone () tiene algunos problemas importantes, y se desaconseja su uso en la mayoría de los casos. Consulte el Artículo 11, de " Java efectivo " de Joshua Bloch para obtener una respuesta completa. Creo que puede usar Object.clone () de forma segura en matrices de tipo primitivo, pero aparte de eso, debe ser prudente sobre el uso y la anulación del clon. Probablemente sea mejor definir un constructor de copias o un método de fábrica estático que clone explícitamente el objeto de acuerdo con su semántica.

Julien Chastang
fuente
15

Creo que esto debería hacer el truco usando la API de Colecciones:

Nota : el método de copia se ejecuta en tiempo lineal.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);
Aaron
fuente
13
¿Por qué no simplemente usar la nueva ArrayList <String> (oldList)?
cdmckay 01 de
44
Creo que esto no funcionaría, desde doc * La lista de destinos debe ser al menos tan larga como la lista de origen. Si es más largo, los elementos restantes en la lista de destino no se verán afectados.
Greg Domjan
@GregDomjan no es que esté de acuerdo en que esta es la mejor manera, pero es una forma de hacerlo. Para solucionar su problema, es tan simple como esto: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz
1
No estoy seguro de si está relacionado con Java 8, pero incluso al especificar el tamaño, sigue obteniendo una excepción IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 en List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Parece que usar copyList.addAll(original);es una buena alternativa
Gene Bo
@GeneBo No especifica el tamaño. Está especificando la capacidad, que es algo muy diferente. La alegría de los intargumentos misteriosos . No tengo idea de por qué quieres usar este oscuro método estático en lugar de uno bueno addAll.
Tom Hawtin - tackline
8

Creo que usar addAll funciona bien.

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

se utilizan paréntesis en lugar de la sintaxis genérica

Allain Lalonde
fuente
55
Eso funcionará para Strings, pero no para objetos mutables. También querrás clonarlos.
jodonnell
Sí, su pregunta es para cuerdas. Y tiene el problema de los genéricos, a los que realmente no les gusta el casting en este caso.
Allain Lalonde
Además, ArrayList.clone solo hará un clon superficial, por lo que los objetos mutables en la lista tampoco se clonarán con ese método.
pkaeding
Eso debería ser ArrayList <String> por cierto. Además, probablemente sea mejor usar el nuevo ArrayList <String> (original), ya que es menos escrito e igual de claro.
cdmckay 01 de
44
¿Por qué no solo usar new ArrayList<String>(original)?
user102008
6

Si desea esto para poder devolver la Lista en un captador, sería mejor hacerlo:

ImmutableList.copyOf(list);
Uri Shalit
fuente
4

Para clonar una interfaz genérica como la java.util.Listque necesita, solo deberá lanzarla. Aquí tienes un ejemplo:

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

Es un poco complicado, pero funciona si está limitado a devolver un List interfaz, por lo que cualquier persona después de usted puede implementar su lista cuando lo desee.

Sé que esta respuesta está cerca de la respuesta final, pero mi respuesta responde cómo hacer todo eso mientras estás trabajando con Listel padre genérico.ArrayList

Ahmed Hamdy
fuente
2
usted está asumiendo que siempre será un ArrayList que no es cierto
jucardi
@JuanCarlosDiaz lo hará, esta es la pregunta que se hizo, se trataba de ArrayList, así que respondí por ArrayList :)
Ahmed Hamdy
2

Tenga mucho cuidado al clonar ArrayLists. La clonación en java es poco profunda. Esto significa que solo clonará el Arraylist y no sus miembros. Entonces, si tiene una ArrayList X1 y la clona en X2, cualquier cambio en X2 también se manifestará en X1 y viceversa. Cuando clones solo generarás una nueva ArrayList con punteros a los mismos elementos en el original.

Juan Besa
fuente
2

Esto también debería funcionar:

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

Tenga en cuenta que esto es solo una copia superficial, no profunda, es decir. obtienes una nueva lista, pero las entradas son las mismas. Esto no es problema para simples cadenas. Obtener es más complicado cuando las entradas de la lista son objetos en sí mismos.

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

No soy un profesional de Java, pero tengo el mismo problema y traté de resolverlo con este método. (Supongamos que T tiene un constructor de copia).

 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
fuente
No hay garantía de que la Listclase de implementación tenga un constructor público sin argumentos. Por ejemplo, el Lists regresa por Array.asList, List.ofo List.subListprobablemente no.
Tom Hawtin - tackline