Ten problem ma dwie typowe przyczyny:
Pola statyczne
Jeśli obiekty na liście przechowują dane w polach statycznych, każdy obiekt na liście będzie wyglądał tak samo, ponieważ mają te same wartości. Rozważ poniższą klasę:
public class Foo {
private static int value;
public Foo(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
W tym przykładzie jest tylko jeden, int value
który jest współużytkowany przez wszystkie wystąpienia, Foo
ponieważ jest zadeklarowany static
. (Zobacz samouczek „Zrozumienie składowych klas” ).
Jeśli dodasz wiele Foo
obiektów do listy za pomocą poniższego kodu, każda instancja zwróci 3
z wywołania getValue()
:
for (int i = 0; i < 4; i++) {
list.add(new Foo(i));
}
Rozwiązanie jest proste - nie używaj static
słów kluczowych dla pól w swojej klasie, chyba że faktycznie chcesz, aby wartości były wspólne dla wszystkich wystąpień tej klasy.
Dodawanie tego samego obiektu
Jeśli dodasz zmienną tymczasową do listy, za każdym razem, gdy wykonujesz pętlę, musisz utworzyć nową instancję dodawanego obiektu. Rozważ następujący błędny fragment kodu:
List<Foo> list = new ArrayList<Foo>();
Foo tmp = new Foo();
for (int i = 0; i < 3; i++) {
tmp.setValue(i);
list.add(tmp);
}
Tutaj tmp
obiekt został skonstruowany poza pętlą. W rezultacie ta sama instancja obiektu jest dodawana do listy trzy razy. Instancja będzie przechowywać wartość 2
, ponieważ była to wartość przekazana podczas ostatniego wywołania funkcji setValue()
.
Aby to naprawić, po prostu przesuń konstrukcję obiektu wewnątrz pętli:
List<Foo> list = new ArrayList<Foo>();
for (int i = 0; i < 3; i++) {
Foo tmp = new Foo();
tmp.setValue(i);
list.add(tmp);
}
tmp
) jest dodawany do listy trzy razy. A ten obiekt ma wartość dwa z powodu wywołaniatmp.setValue(2)
w ostatniej iteracji pętli.you must create a new instance each time you loop
w sekcjiAdding the same object
: Zwróć uwagę, że wystąpienie, o którym mowa, dotyczy obiektu, który dodajesz, a NIE obiektu, do którego go dodajesz.Twój problem dotyczy typu,
static
który wymaga nowej inicjalizacji za każdym razem, gdy pętla jest iterowana. Jeśli jesteś w pętli, lepiej jest zachować konkretną inicjalizację wewnątrz pętli.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); }
Zamiast:
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 }
Oto
tag
zmienna w służącaSomeStaticClass
do sprawdzenia poprawności powyższego fragmentu; możesz mieć inną implementację w oparciu o Twój przypadek użycia.źródło
static
”? Czym byłaby dla ciebie klasa niestatyczna?public class SomeClass{/*some code*/}
& statyczny:public static class SomeStaticClass{/*some code*/}
. Mam nadzieję, że teraz jest to jaśniejsze.Miałem ten sam problem z wystąpieniem kalendarza.
Niepoprawny kod:
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); }
Musisz stworzyć NOWY obiekt kalendarza, co można zrobić za pomocą
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); }
źródło
Za każdym razem, gdy dodajesz obiekt do ArrayList, upewnij się, że dodajesz nowy obiekt, a nie już używany obiekt. Dzieje się tak, że kiedy dodajesz tę samą 1 kopię obiektu, ten sam obiekt jest dodawany do różnych pozycji w ArrayList. A kiedy wprowadzisz zmianę w jednym, ponieważ ta sama kopia jest dodawana w kółko, wszystkie kopie ulegną zmianie. Na przykład załóżmy, że masz taką ArrayList:
ArrayList<Card> list = new ArrayList<Card>(); Card c = new Card();
Teraz, jeśli dodasz tę kartę c do listy, zostanie dodana bez problemu. Zostanie on zapisany w lokalizacji 0. Ale kiedy zapiszesz tę samą Kartę c na liście, zostanie ona zapisana w lokalizacji 1. Pamiętaj więc, że dodałeś ten sam 1 obiekt do dwóch różnych lokalizacji na liście. Teraz, jeśli zmienisz ten obiekt karty c, obiekty na liście w lokalizacji 0 i 1 również odzwierciedlą tę zmianę, ponieważ są tym samym obiektem.
Jednym z rozwiązań byłoby stworzenie konstruktora w klasie Card, który akceptuje inny obiekt Card. Następnie w tym konstruktorze możesz ustawić właściwości w następujący sposób:
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 }
I powiedzmy, że masz tę samą 1 kopię Karty, więc podczas dodawania nowego obiektu możesz zrobić to:
list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));
źródło
Może to również być konsekwencją użycia tego samego odniesienia zamiast używania nowego.
List<Foo> list = new ArrayList<Foo>(); setdata(); ...... public void setdata(int i) { Foo temp = new Foo(); tmp.setValue(i); list.add(tmp); }
Zamiast:
List<Foo> list = new ArrayList<Foo>(); Foo temp = new Foo(); setdata(); ...... public void setdata(int i) { tmp.setValue(i); list.add(tmp); }
źródło