Вопрос-ответ

Why does my ArrayList contain N copies of the last item added to the list?

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

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

Например:

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

Ожидается:

0
1
2

Актуально:

2
2
2

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

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

Переведено автоматически
Ответ 1

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


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



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



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

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

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);
}
Ответ 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, убедитесь, что вы добавляете новый объект, а не уже используемый объект. Происходит то, что когда вы добавляете одну и ту же копию object , этот же объект добавляется на разные позиции в ArrayList. И когда вы вносите изменения в одну из них, поскольку одна и та же копия добавляется снова и снова, затрагиваются все копии. Например, предположим, у вас есть ArrayList, подобный этому:

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

Теперь, если вы добавите эту карту c в список, она будет добавлена без проблем. Она будет сохранена в местоположении 0. Но когда вы сохраните ту же карту c в списке, она будет сохранена в местоположении 1. Итак, помните, что вы добавили один и тот же объект 1 в два разных места в списке. Теперь, если вы внесете изменение в объект Card 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
}

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

list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));
2024-02-28 02:41 java