Copia de Java ArrayList

214

Tengo una ArrayList l1de tamaño 10. Asigno l1al nuevo tipo de referencia de lista l2. Will l1y l2apunta al mismo ArrayListobjeto? ¿O se le ArrayListasigna una copia del objeto l2?

Cuando uso la l2referencia, si actualizo el objeto de la lista, también refleja los cambios en el l1tipo de referencia.

Por ejemplo:

List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
    l1.add(i);
}

List l2 = l1;
l2.clear();

¿No hay otra manera de asignar una copia de un objeto de lista a una nueva variable de referencia, además de crear 2 objetos de lista y hacer una copia en colecciones de lo antiguo a lo nuevo?

usuario309281
fuente

Respuestas:

460

Sí, la asignación solo copiará el valor de l1(que es una referencia) l2. Ambos se referirán al mismo objeto.

Sin embargo, crear una copia superficial es bastante fácil:

List<Integer> newList = new ArrayList<>(oldList);

(Solo como un ejemplo).

Jon Skeet
fuente
1
¿Es posible copiar solo una parte de una lista de arrays a una nueva lista de arrays, de manera eficiente? por ejemplo: copie elementos entre la posición 5 y 10 de una lista de matrices a otra nueva. En mi aplicación, el rango sería mucho mayor.
Ashwin
1
@Ashwin: Bueno, es una operación O (N), pero sí ... puedes usarla List.subListpara obtener una "vista" en una sección de la lista original.
Jon Skeet
¿Qué pasa si las listas de matriz están anidadas (ArrayList<ArrayList<Object>>)? ¿esto crearía recursivamente copias de todos los objetos hijos de ArrayList?
Gato
3
@ Cat: No ... Esta es solo una copia superficial.
Jon Skeet
1
@ShanikaEdiriweera: Sí, podrías hacerlo de forma fluida con las transmisiones. Pero la parte difícil es crear una copia profunda, que la mayoría de los objetos no proporcionarán. Si tiene un caso específico en mente, le sugiero que haga una nueva pregunta con detalles.
Jon Skeet
67

Tratar de usar Collections.copy(destination, source);

Sergii Zagriichuk
fuente
14
¿Le gustaría explicar por qué esto es preferible new ArrayList<>(source);?
Alex
66
@atc es una forma más de hacer una copia superficial, en lugar de la nueva ArrayList () se usa otro algoritmo y se puede usar para cualquier implementación de Lista, no solo para una ArrayList, eso es todo :)
Sergii Zagriichuk
14
¡Este método es muy engañoso! En realidad, su descripción es. Dice: "copia elementos de una lista de origen en el destino", ¡pero no se copian! Están referenciados por lo que solo habrá 1 copia de los objetos y si son mutables, estás en problemas
ACV
55
ninguna clase de colección realiza la clonación profunda de java-api
Vikash
Esta respuesta no tiene mucho sentido. Collections.copyno es en absoluto una alternativa a new ArrayList<>(source). Lo que Collections.copyrealmente hace es asumir que destination.size()es al menos tan grande como source.size(), y luego copiar el rango índice por índice usando el set(int,E)método El método no agrega nuevos elementos al destino. Consulte el código fuente si no está lo suficientemente claro en el Javadoc.
Radiodef
35

l1y l2apuntará a la misma referencia, el mismo objeto.

Si desea crear una nueva ArrayList basada en la otra ArrayList, haga esto:

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");

El resultado será l1que todavía tendrá 2 elementos y l2tendrá 3 elementos.

Alfredo Osorio
fuente
¿Puedes explicar la diferencia entre List<String> l2 = new ArrayList<String>(l1)y List<String> l2 = l1?
MortalMan
@MortalMan, la diferencia es que l2 = new ArrayList <String> (l1) es un objeto completamente nuevo y la modificación de l2 no afecta a l1, mientras que List <String> l2 = l1 no está creando un nuevo objeto sino simplemente haciendo referencia al mismo como l1, por lo que en este caso realizar una operación como l2.add ("Everybody"), l1.size () y l2.size () devolverá 3 porque ambos hacen referencia al mismo objeto.
Alfredo Osorio
19

Otra forma conveniente de copiar los valores de src ArrayList a dest Arraylist es la siguiente:

ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);

Esta es una copia real de valores y no solo una copia de referencia.

Harshal Waghmare
fuente
10
No estoy completamente seguro de que esto sea exacto. mi prueba muestra lo contrario (todavía hace referencia al mismo objeto)
invertigo
esta solución funcionó para mí cuando uso ArrayList con ArrayAdapter
albanx
1
Esta respuesta es incorrecta. addAll () solo copia las referencias como dijo invertigo. Esta no es una copia profunda.
jk7
Para una ArrayList <String>, esta respuesta es aceptable porque String es inmutable, pero pruébelo con el ejemplo del OP, una ArraList <Integer>, y verá que solo está copiando referencias.
jk7
Simplemente no es mi día, supongo. Resulta que clases como Integer y Long también son inmutables, por lo que la respuesta de Harshal funciona para casos simples como ArrayList <Integer> y ArrayList <String>. Donde falla es para objetos complejos que no son inmutables.
jk7
8

Hay un método addAll () que servirá para copiar One ArrayList a otro.

Por ejemplo, tiene dos listas de matrices : sourceList y targetList , use el código a continuación.

targetList.addAll (sourceList);

vaibhav agrawal
fuente
También solo copia referencias.
Vikash
4

Java no pasa objetos, pasa referencias (punteros) a objetos. Entonces sí, l2 y l1 son dos punteros al mismo objeto.

Debe hacer una copia explícita si necesita dos listas diferentes con el mismo contenido.

JB Nizet
fuente
3
¿Cómo se hace "una copia explícita"? ¿Supongo que estás hablando de una copia profunda?
Cin316
1

List.copyOf ➙ lista no modificable

Tu preguntaste:

¿No hay otra forma de asignar una copia de una lista?

Java 9 trajo los List.ofmétodos para usar literales para crear una Listclase concreta inmodificable de desconocida.

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of( 
    today.minusDays( 1 ) ,  // Yesterday
    today ,                 // Today
    today.plusDays( 1 )     // Tomorrow
);

Junto con eso también tenemos List.copyOf. Este método también devuelve una Listclase de hormigón desconocido no modificable .

List< String > colors = new ArrayList<>( 4 ) ;          // Creates a modifiable `List`. 
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ;   // Creates an unmodifiable `List`.

Por "no modificable" nos referimos al número de elementos en la lista, y el referente del objeto que se mantiene en cada espacio como elemento, es fijo. No puede agregar, quitar o reemplazar elementos. Pero el referente del objeto contenido en cada elemento puede o no ser mutable .

colors.remove( 2 ) ;          // SUCCEEDS. 
masterColors.remove( 2 ) ;    // FAIL - ERROR.

Vea este código en vivo en IdeOne.com .

date.toString (): [2020-02-02, 2020-02-03, 2020-02-04]

colors.toString (): [AliceBlue, PapayaWhip, DarkSlateGray]

masterColors.toString (): [AliceBlue, PapayaWhip, Chartreuse, DarkSlateGray]

Preguntaste sobre referencias de objetos. Como otros dijeron, si crea una lista y la asigna a dos variables de referencia (punteros), todavía tiene solo una lista. Ambos apuntan a la misma lista. Si usa cualquiera de los punteros para modificar la lista, ambos punteros verán más tarde los cambios, ya que solo hay una lista en la memoria.

Entonces necesita hacer una copia de la lista. Si desea que esa copia no sea modificable, utilice el List.copyOfmétodo como se describe en esta Respuesta. En este enfoque, terminas con dos listas separadas, cada una con elementos que contienen una referencia a los mismos objetos de contenido. Por ejemplo, en nuestro ejemplo anterior usando Stringobjetos para representar colores, los objetos de color están flotando en alguna parte de la memoria. Las dos listas contienen punteros a los mismos objetos de color. Aquí hay un diagrama.

ingrese la descripción de la imagen aquí

La primera lista colorses modificable. Esto significa que algunos elementos podrían eliminarse como se ve en el código anterior, donde eliminamos el tercer elemento original Chartreuse(índice de 2 = ordinal 3). Y se pueden agregar elementos. Y los elementos se pueden cambiar para que apunten a otros Stringcomo OliveDrabo CornflowerBlue.

En contraste, los cuatro elementos de masterColorsson fijos. Sin eliminación, sin adición y sin sustitución de otro color. Esa Listimplementación no se puede modificar.

Albahaca Bourque
fuente