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
Эта проблема имеет две типичные причины:
Статические поля, используемые объектами, которые вы сохранили в списке
Случайное добавление того же объекта в список
Статические поля
Если объекты в вашем списке хранят данные в статических полях, каждый объект в вашем списке будет выглядеть одинаково, потому что они содержат одинаковые значения. Рассмотрим класс ниже:
publicclassFoo { privatestaticint value; // ^^^^^^------------ - Here's the problem!
publicFoo(int value) { this.value = value; }
publicintgetValue() { return value; } }
В этом примере есть только одна, int value которая является общей для всех экземпляров Foo, потому что она объявлена static. (См. "Понимание членов класса" учебное пособие.)
Если вы добавите несколько Foo объектов в список, используя приведенный ниже код, каждый экземпляр будет возвращать 3 результат вызова getValue():
for (inti=0; i < 4; i++) { list.add(newFoo(i)); }
Решение простое - не используйте static ключевые слова для полей в вашем классе, если вы действительно не хотите, чтобы значения были общими для каждого экземпляра этого класса.
Добавление того же объекта
Если вы добавляете временную переменную в список, вы должны создавать новый экземпляр добавляемого объекта каждый раз при выполнении цикла. Рассмотрим следующий фрагмент кода с ошибкой:
List<Foo> list = newArrayList<Foo>(); Footmp=newFoo();
for (inti=0; i < 3; i++) { tmp.setValue(i); list.add(tmp); }
Здесь tmp объект был создан вне цикла. В результате один и тот же экземпляр объекта добавляется в список три раза. Экземпляр будет содержать значение 2, потому что это было значение, переданное во время последнего вызова setValue().
Чтобы исправить это, просто переместите конструкцию объекта внутри цикла:
List<Foo> list = newArrayList<Foo>();
for (inti=0; i < 3; i++) { Footmp=newFoo(); // <-- fresh instance! tmp.setValue(i); list.add(tmp); }
Ответ 2
Ваша проблема связана с типом, static который требует новой инициализации каждый раз при повторении цикла. Если вы находитесь в цикле, лучше сохранить конкретную инициализацию внутри цикла.
List<Object> objects = newArrayList<>();
for (inti=0; i < length_you_want; i++) { SomeStaticClassmyStaticObject=newSomeStaticClass(); myStaticObject.tag = i; // Do stuff with myStaticObject objects.add(myStaticClass); }
Вместо:
List<Object> objects = newArrayList<>();
SomeStaticClassmyStaticObject=newSomeStaticClass(); for (inti=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
Была такая же проблема с экземпляром календаря.
Неправильный код:
CalendarmyCalendar= Calendar.getInstance();
for (intdays=0; days < daysPerWeek; days++) { myCalendar.add(Calendar.DAY_OF_YEAR, 1);
// In the next line lies the error CalendarnewCal= myCalendar; calendarList.add(newCal); }
Вам нужно создать НОВЫЙ объект календаря, что можно сделать с помощью calendar.clone();
CalendarmyCalendar= Calendar.getInstance();
for (intdays=0; days < daysPerWeek; days++) { myCalendar.add(Calendar.DAY_OF_YEAR, 1);
// RIGHT WAY CalendarnewCal= (Calendar) myCalendar.clone(); calendarList.add(newCal);
}
Ответ 4
Каждый раз, когда вы добавляете объект в ArrayList, убедитесь, что вы добавляете новый объект, а не уже используемый объект. Происходит то, что когда вы добавляете одну и ту же копию object , этот же объект добавляется на разные позиции в ArrayList. И когда вы вносите изменения в одну из них, поскольку одна и та же копия добавляется снова и снова, затрагиваются все копии. Например, предположим, у вас есть ArrayList, подобный этому:
ArrayList<Card> list = newArrayList<Card>(); Cardc=newCard();
Теперь, если вы добавите эту карту c в список, она будет добавлена без проблем. Она будет сохранена в местоположении 0. Но когда вы сохраните ту же карту c в списке, она будет сохранена в местоположении 1. Итак, помните, что вы добавили один и тот же объект 1 в два разных места в списке. Теперь, если вы внесете изменение в объект Card c, объекты в списке в местоположениях 0 и 1 также отразят это изменение, потому что это один и тот же объект.
Одним из решений было бы создать конструктор в классе Card, который принимает другой объект Card. Затем в этом конструкторе вы можете установить свойства следующим образом:
publicCard(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, поэтому во время добавления нового объекта вы можете сделать это: