¿Por qué mi Lista de Arreglos contiene N copias del último elemento añadido a la lista

java list arraylist static


Estoy agregando tres objetos diferentes a una lista de arreglos,pero la lista contiene tres copias del último objeto que agregué.

Por ejemplo:

for (Foo f : list) {
  System.out.println(f.getValue());
}    

Expected:

0
1
2

Actual:

2
2
2

¿Qué error he cometido?

Nota: esto está diseñado para ser un Q&A canónico para los numerosos problemas similares que surgen en este sitio.




Answer 1 Duncan Jones


Este problema tiene dos causas típicas:

  • Los campos estáticos utilizados por los objetos almacenados en la lista

  • Agregar accidentalmente el mismo objeto a la lista

Campos estáticos

Si los objetos de su lista almacenan datos en campos estáticos,cada objeto de su lista parecerá ser el mismo,porque tienen los mismos valores.Considere la clase que aparece a continuación:

public class Foo {
  private static int value; 
  //      ^^^^^^------------ - Here's the problem!

  public Foo(int value) {
    this.value = value;
  }

  public int getValue() {
    return value;
  }
}

En ese ejemplo, solo hay un int value que se comparte entre todas las instancias de Foo porque se declara static . (Consulte el tutorial "Comprender a los miembros de la clase" ).

Si agrega múltiples objetos Foo a una lista usando el código a continuación, cada instancia devolverá 3 de una llamada a getValue() :

for (int i = 0; i < 4; i++) {      
  list.add(new Foo(i));
}

La solución es simple: no use las palabras clave static para los campos de su clase a menos que realmente desee que los valores se compartan entre cada instancia de esa clase.

Añadiendo el mismo objeto

Si agregas una variable temporal a una lista,debes crear una nueva instancia del objeto que estás agregando,cada vez que haces un bucle.Considere el siguiente fragmento de código erróneo:

List<Foo> list = new ArrayList<Foo>();    
Foo tmp = new Foo();

for (int i = 0; i < 3; i++) {
  tmp.setValue(i);
  list.add(tmp);
}

Aquí, el objeto tmp se construyó fuera del bucle. Como resultado, la misma instancia de objeto se agrega a la lista tres veces. La instancia mantendrá el valor 2 , porque ese fue el valor pasado durante la última llamada a setValue() .

Para arreglar esto,sólo hay que mover la construcción del objeto dentro del bucle:

List<Foo> list = new ArrayList<Foo>();        

for (int i = 0; i < 3; i++) {
  Foo tmp = new Foo(); // <-- fresh instance!
  tmp.setValue(i);
  list.add(tmp);
}



Answer 2 Shashank


Su problema es con el tipo static que requiere una nueva inicialización cada vez que se itera un ciclo. Si está en un bucle, es mejor mantener la inicialización concreta dentro del bucle.

List<Object> objects = new ArrayList<>(); 

for (int i = 0; i < length_you_want; i++) {
    SomeStaticClass myStaticObject = new SomeStaticClass();
    myStaticObject.tag = i;
    // Do stuff with myStaticObject
    objects.add(myStaticClass);
}

En lugar de:

List<Object> objects = new ArrayList<>(); 

SomeStaticClass myStaticObject = new SomeStaticClass();
for (int i = 0; i < length; i++) {
    myStaticObject.tag = i;
    // Do stuff with myStaticObject
    objects.add(myStaticClass);
    // This will duplicate the last item "length" times
}

Aquí la tag es una variable en SomeStaticClass para verificar la validez del fragmento anterior; puede tener alguna otra implementación basada en su caso de uso.




Answer 3 basti12354


Tuvo el mismo problema con la instancia del calendario.

Código equivocado:

Calendar myCalendar = Calendar.getInstance();

for (int days = 0; days < daysPerWeek; days++) {
    myCalendar.add(Calendar.DAY_OF_YEAR, 1);

    // In the next line lies the error
    Calendar newCal = myCalendar;
    calendarList.add(newCal);
}

Debe crear un NUEVO objeto del calendario, que se puede hacer con calendar.clone() ;

Calendar myCalendar = Calendar.getInstance();

for (int days = 0; days < daysPerWeek; days++) {
    myCalendar.add(Calendar.DAY_OF_YEAR, 1);

    // RIGHT WAY
    Calendar newCal = (Calendar) myCalendar.clone();
    calendarList.add(newCal);

}



Answer 4 Faraz


Cada vez que agregue un objeto a una lista de arreglos,asegúrese de agregar un nuevo objeto y no un objeto ya usado.Lo que sucede es que cuando añades la misma copia de un objeto,ese mismo objeto se añade a diferentes posiciones en una ArrayList.Y cuando haces un cambio en una,porque la misma copia se añade una y otra vez,todas las copias se ven afectadas.Por ejemplo,digamos que tienes una ArrayList como esta:

ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();

Ahora,si agregas esta tarjeta c a la lista,se agregará sin problema.Se guardará en la ubicación 0.Pero,cuando guardes la misma Carta c en la lista,se guardará en la ubicación 1.Así que recuerda que has añadido el mismo objeto 1 a dos lugares diferentes de la lista.Ahora,si haces un cambio en el objeto de la Carta c,los objetos de la lista en la posición 0 y 1 también reflejarán ese cambio,porque son el mismo objeto.

Una solución sería hacer un constructor en la clase de Carta,que acepte otro objeto de Carta.Entonces en ese constructor,puedes establecer las propiedades de esta manera:

public Card(Card c){
this.property1 = c.getProperty1();
this.property2 = c.getProperty2(); 
... //add all the properties that you have in this class Card this way
}

Y digamos que tienes la misma 1 copia de Card,así que al momento de agregar un nuevo objeto,puedes hacer esto:

list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));



Answer 5 Shaikh Mohib


También puede ser consecuencia de usar la misma referencia en lugar de usar una nueva.

 List<Foo> list = new ArrayList<Foo>();        

 setdata();
......

public void setdata(int i) {
  Foo temp = new Foo();
  tmp.setValue(i);
  list.add(tmp);
}

En lugar de:

List<Foo> list = new ArrayList<Foo>(); 
Foo temp = new Foo();       
setdata();
......

public void setdata(int i) {
  tmp.setValue(i);
  list.add(tmp);
}