![](/img/trans.png)
[英]Storing combinations of an array in a list, contains N copies of last item added
[英]Why does my ArrayList contain N copies of the last item added to the list?
我正在向 ArrayList 添加三個不同的對象,但該列表包含我添加的最后一個 object 的三個副本。
例如:
for (Foo f : list) {
System.out.println(f.getValue());
}
預期的:
0
1
2
實際的:
2
2
2
我犯了什么錯誤?
注意:這是針對本網站上出現的眾多類似問題的規范問答。
此問題有兩個典型原因:
您存儲在列表中的對象使用的靜態字段
不小心將相同的對象添加到列表中
如果列表中的對象將數據存儲在靜態字段中,則列表中的每個對象看起來都是相同的,因為它們具有相同的值。 考慮下面的類:
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
對象添加到列表中,每個實例將從對getValue()
的調用中返回3
:
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);
}
您的問題是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
中的一個變量,用於檢查上述代碼段的有效性; 您可以根據您的用例進行一些其他實現。
日歷實例也有同樣的問題。
錯誤代碼:
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);
}
每次將對象添加到 ArrayList 時,請確保添加一個新對象而不是尚未使用的對象。 發生的情況是,當您添加相同的 1 個對象副本時,該對象將添加到 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));
它也可能是使用相同引用而不是使用新引用的結果。
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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.