Почему мой ArrayList содержит N копий последнего элемента,добавленного в список.

java list arraylist static


Я добавляю три разных объекта в ArrayList,но список содержит три копии последнего добавленного мною объекта.

Например:

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

Expected:

0
1
2

Actual:

2
2
2

Какую ошибку я совершил?

Примечание: это разработано, чтобы быть каноническим Q & A для многочисленных подобных проблем, которые возникают на этом сайте.




Answer 1 Duncan Jones


У этой проблемы две типичные причины:

  • Статические поля,используемые объектами,которые вы сохранили в списке

  • Случайно добавив тот же объект в список

Статические поля

Если объекты в вашем списке хранят данные в статических полях,каждый объект в списке будет выглядеть одинаково,потому что они содержат одинаковые значения.Рассмотрим класс ниже:

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

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

  public int getValue() {
    return value;
  }
}

В этом примере есть только одно int value которое используется всеми экземплярами Foo , потому что оно объявлено как static . (См. Учебник «Понимание членов класса» .)

Если вы добавите несколько объектов Foo в список с помощью приведенного ниже кода, каждый экземпляр вернет 3 из вызова getValue() :

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

Решение простое - не используйте static ключевые слова для полей в вашем классе, если вы на самом деле не хотите, чтобы значения разделялись между каждым экземпляром этого класса.

Добавление того же объекта

Если вы добавляете временную переменную в список,вы должны создавать новый экземпляр добавляемого объекта каждый раз при цикле.Рассмотрим следующий фрагмент ошибочного кода:

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

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

Здесь объект tmp был создан вне цикла. В результате один и тот же экземпляр объекта добавляется в список три раза. Экземпляр будет содержать значение 2 , потому что это было значение, переданное во время последнего вызова setValue() .

Чтобы это исправить,просто переместите конструкцию объекта внутрь петли:

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


Ваша проблема связана с типом static , который требует новой инициализации при каждой итерации цикла. Если вы находитесь в цикле, лучше сохранить конкретную инициализацию внутри цикла.

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);
}

Вместо этого:

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
}

Здесь tag - переменная в SomeStaticClass для проверки правильности приведенного выше фрагмента; у вас может быть другая реализация, основанная на вашем случае использования.




Answer 3 basti12354


У меня были те же проблемы с экземпляром календаря.

Неверный код:

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);
}

Вам необходимо создать НОВЫЙ объект календаря, что можно сделать с помощью 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


Каждый раз,когда вы добавляете объект в ArrayList,убедитесь,что вы добавили новый объект,а не уже использованный.Происходит то,что когда вы добавляете одну и ту же 1 копию объекта,этот же объект добавляется в разные позиции в массиве.А когда вы вносите изменение в одну,потому что одна и та же копия добавляется снова и снова,это влияет на все копии.Например,скажем,у вас есть такой массив:

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

Теперь,если вы добавите эту Карту c в список,она будет добавлена без проблем.Она будет сохранена в месте 0.Но,если вы сохраните ту же самую Карту c в списке,то она будет сохранена в месте 1.Поэтому помните,что вы добавили один и тот же объект в два разных места в списке.Теперь,если вы измените объект карты c,объекты в списке в точке 0 и 1 также будут отражать это изменение,потому что они являются одним и тем же объектом.

Одним из решений будет создание конструктора в классе Card,принимающего другой объект Card.Затем в этом конструкторе можно задать такие свойства:

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
}

И допустим,у вас одна и та же копия Карты,так что во время добавления нового объекта,вы можете это сделать:

list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));



Answer 5 Shaikh Mohib


Это также может быть следствием использования одной и той же ссылки вместо новой.

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

 setdata();
......

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

Вместо этого:

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

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