¿Cómo clonar ArrayList y también clonar sus contenidos?

¿Cómo puedo clonar una ArrayList y también clonar sus elementos en Java?

Por ejemplo, tengo:

 ArrayList dogs = getDogs(); ArrayList clonedList = ....something to do with dogs.... 

Y yo esperaría que los objetos en clonedList no son los mismos que en la lista de perros.

Tendrá que repetir los elementos y clonarlos uno por uno, colocando los clones en la matriz de resultados sobre la marcha.

 public static List cloneList(List list) { List clone = new ArrayList(list.size()); for (Dog item : list) clone.add(item.clone()); return clone; } 

Para que eso funcione, obviamente, tendrás que obtener tu objeto Dog para implementar la interfaz Cloneable y el método clone ().

Yo, personalmente, agregaría un constructor a Dog:

 class Dog { public Dog() { ... } // Regular constructor public Dog(Dog dog) { // Copy all the fields of Dog. } } 

Luego simplemente itere (como se muestra en la respuesta de Varkhan):

 public static List cloneList(List dogList) { List clonedList = new ArrayList(dogList.size()); for (Dog dog : dogList) { clonedList.add(new Dog(dog)); } return clonedList; } 

Encuentro que la ventaja de esto es que no necesitas atornillar con las cosas clonables rotas en Java. También coincide con la forma en que copias las colecciones de Java.

Otra opción podría ser escribir su propia interfaz ICloneable y usarla. De esa forma podrías escribir un método genérico para la clonación.

Todas las colecciones estándar tienen constructores de copias. Usalos, usalos a ellos.

 List original = // some list List copy = new ArrayList(original); //This does a shallow copy 

clone() fue diseñado con varios errores (ver esta pregunta ), así que es mejor evitarlo.

De Effective Java 2nd Edition , Item 11: Anular el clon juiciosamente

Dado todos los problemas asociados con Cloneable, es seguro decir que otras interfaces no deberían extenderlo, y que las clases diseñadas para la herencia (Ítem 17) no deberían implementarlo. Debido a sus muchas deficiencias, algunos progtwigdores expertos simplemente eligen nunca anular el método de clonación y nunca invocarlo, excepto, tal vez, para copiar matrices. Si diseña una clase para la herencia, tenga en cuenta que si elige no proporcionar un método de clonación protegido con buen comportamiento, será imposible que las subclases implementen Clonación.

Este libro también describe las muchas ventajas que los constructores de copia tienen sobre Cloneable / clone.

  • No se basan en un mecanismo extralingüístico de creación de objetos propenso a riesgos
  • No exigen una adhesión inaplicable a las convenciones finamente documentadas
  • No entran en conflicto con el uso adecuado de los campos finales
  • No lanzan excepciones marcadas innecesarias
  • No requieren moldes.

Considere otro beneficio de usar constructores de copia: HashSet s que tiene un HashSet s y quiere copiarlo como un TreeSet . El método de clonación no puede ofrecer esta funcionalidad, pero es fácil con un constructor de conversión: new TreeSet(s) .

Java 8 proporciona una nueva forma de llamar al constructor de copias o al método de clonación en los elementos de perros de forma elegante y compacta: secuencias , lambdas y colectores .

Copiar constructor:

 List clonedDogs = dogs.stream().map(Dog::new).collect(toList()); 

La expresión Dog::new se llama referencias a un método . Hace referencia a un constructor en Dog que toma otro perro como argumento.

Método de clonación

 List clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList()); 

Obtener una ArrayList como resultado

O bien, si tiene que recuperar una ArrayList (en caso de que quiera modificarla más adelante):

 ArrayList clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new)); 

Actualiza la lista en su lugar

Si no necesita conservar el contenido original de la lista de dogs , puede usar el método replaceAll y actualizar la lista en su lugar:

 dogs.replaceAll(Dog::new); 

Todos los ejemplos suponen import static java.util.stream.Collectors.*; .


Recostackdor para ArrayList s

El recostackdor del último ejemplo se puede convertir en un método util. Dado que esto es algo tan común de hacer, personalmente me gusta que sea corto y bonito. Me gusta esto:

 ArrayList clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList()); public static  Collector> toArrayList() { return Collectors.toCollection(ArrayList::new); } 

Nota sobre CloneNotSupportedException :

Para que esta solución funcione, el método clone de Dog no debe declarar que arroja CloneNotSupportedException . La razón es que el argumento para map no tiene permitido lanzar ninguna excepción marcada.

Me gusta esto:

  // Note: Method is public and returns Dog, not Object @Override public Dog clone() /* Note: No throws clause here */ { ... 

Sin embargo, esto no debería ser un gran problema, ya que esa es la mejor práctica de todos modos. ( Effectice Java, por ejemplo, da este consejo).

Gracias a Gustavo por notar esto.


PD:

Si le parece más bonito, puede usar la syntax de referencia del método para hacer exactamente lo mismo:

 List clonedDogs = dogs.stream().map(Dog::clone).collect(toList()); 

Básicamente hay tres formas sin iterar manualmente

1 Usando constructor

 ArrayList dogs = getDogs(); ArrayList clonedList = new ArrayList(dogs); 

2 Usando addAll(Collection c)

 ArrayList dogs = getDogs(); ArrayList clonedList = new ArrayList(); clonedList.addAll(dogs); 

3 Uso del método addAll(int index, Collection c) con el parámetro int

 ArrayList dogs = getDogs(); ArrayList clonedList = new ArrayList(); clonedList.addAll(0, dogs); 

NB: el comportamiento de estas operaciones no estará definido si la colección especificada se modifica mientras la operación está en progreso.

Creo que la respuesta verde actual es mala , ¿por qué puedes preguntar?

  • Puede requerir agregar mucho código
  • Requiere que liste todas las Listas que se copiarán y haga esto

La forma en que la serialización también es mala, tal vez tengas que agregar Serializable por todas partes.

Entonces, cuál es la solución:

Biblioteca de clonación profunda de Java La biblioteca de clonación es una biblioteca de java pequeña, de código abierto (licencia de apache) que clona objetos en profundidad. Los objetos no tienen que implementar la interfaz Cloneable. Efectivamente, esta biblioteca puede clonar CUALQUIER objeto Java. Se puede usar, por ejemplo, en implementaciones de caché, si no desea que se modifique el objeto en caché o cuando quiera crear una copia profunda de los objetos.

 Cloner cloner=new Cloner(); XX clone = cloner.deepClone(someObjectOfTypeXX); 

Compruébelo en https://github.com/kostaskougios/cloning

Tendrá que clonar ArrayList a mano (iterando sobre él y copiando cada elemento en una nueva ArrayList ), porque clone() no lo hará por usted. La razón de esto es que los objetos contenidos en ArrayList no pueden implementar Clonable ellos mismos.

Editar : … y eso es exactamente lo que hace el código de Varkhan.

Una forma desagradable es hacerlo con reflexión. Algo como esto funcionó para mí.

 public static  List deepCloneList(List original) { if (original == null || original.size() < 1) { return new ArrayList<>(); } try { int originalSize = original.size(); Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone"); List clonedList = new ArrayList<>(); // noinspection ForLoopReplaceableByForEach for (int i = 0; i < originalSize; i++) { // noinspection unchecked clonedList.add((T) cloneMethod.invoke(original.get(i))); } return clonedList; } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { System.err.println("Couldn't clone list due to " + e.getMessage()); return new ArrayList<>(); } } 

Los otros carteles son correctos: necesita repetir la lista y copiar en una nueva lista.

Sin embargo … Si los objetos de la lista son inmutables, no es necesario clonarlos. Si su objeto tiene un gráfico de objeto complejo, también deberán ser inmutables.

El otro beneficio de la inmutabilidad es que también son seguros para los hilos.

Aquí hay una solución que usa un tipo de plantilla genérica:

 public static  List copyList(List source) { List dest = new ArrayList(); for (T item : source) { dest.add(item); } return dest; } 

para que los objetos anulen el método clone ()

 class You_class { int a; @Override public You_class clone() { You_class you_class = new You_class(); you_class.a = this.a; return you_class; } } 

y llama a .clone () para Vector obj o ArraiList obj ….

Manera fácil mediante el uso de commons-lang-2.3.jar esa biblioteca de java a la lista de clones

enlace descargar commons-lang-2.3.jar

Cómo utilizar

 oldList......... List newList = new ArrayList(); foreach(YourObject obj : oldList){ newList.add((YourObject)SerializationUtils.clone(obj)); } 

Espero que este pueda ser útil.

:RE

El paquete import org.apache.commons.lang.SerializationUtils;

Hay un método SerializationUtils.clone(Object);

Ejemplo

 this.myObjectCloned = SerializationUtils.clone(this.object); 

Acabo de desarrollar una lib que pueda clonar un objeto de entidad y un objeto java.util.List. Simplemente descargue el archivo jar en https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U y utilice el método estático cloneListObject (lista de listas). Este método no solo clona la Lista, sino también todos los elementos de la entidad.

He usado esta opción siempre:

 ArrayList clonedList = new ArrayList(name_of_arraylist_that_you_need_to_Clone); 

He encontrado una manera, puedes usar json para serializar / deserializar la lista. La lista serializada no contiene ninguna referencia al objeto original cuando se deserializa.

Usando gson:

 List originalList = new ArrayList<>(); // add some items later String listAsJson = gson.toJson(originalList); List newList = new Gson().fromJson(listAsJson, new TypeToken>() {}.getType()); 

Puedes hacer eso usando jackson y cualquier otra biblioteca json también.

Creo que encontré una manera realmente fácil de hacer una copia profunda de ArrayList. Suponiendo que desea copiar una matriz String ArrayListA.

 ArrayListarrayB = new ArrayList(); arrayB.addAll(arrayA); 

Avíseme si no funciona para usted.