Подтвердить что ты не робот

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

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

Например:

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

Ожидаемое:

0
1
2

Actual:

2
2
2

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

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

4b9b3361

Ответ 1

Эта проблема имеет две типичные причины:

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

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

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

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

public class Foo {
  private static int value; 
  //      ^^^^^^------------ - Here 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);
}

Ответ 2

Ваша проблема связана с типом 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, чтобы проверить достоверность приведенного выше фрагмента; вы можете иметь некоторую другую реализацию на основе вашего варианта использования.

Ответ 3

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

Неправильный код:

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

}

Ответ 4

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

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

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

Одним из решений было бы сделать конструктор класса 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
}

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

list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));