【发布时间】:2025-11-24 12:40:01
【问题描述】:
我正在向 ArrayList 添加三个不同的对象,但该列表包含我添加的最后一个对象的三个副本。
例如:
for (Foo f : list) {
System.out.println(f.getValue());
}
预期:
0
1
2
实际:
2
2
2
我犯了什么错误?
注意:这是针对本网站上出现的许多类似问题的规范问答。
【问题讨论】:
标签: java
我正在向 ArrayList 添加三个不同的对象,但该列表包含我添加的最后一个对象的三个副本。
例如:
for (Foo f : list) {
System.out.println(f.getValue());
}
预期:
0
1
2
实际:
2
2
2
我犯了什么错误?
注意:这是针对本网站上出现的许多类似问题的规范问答。
【问题讨论】:
标签: java
这个问题有两个典型的原因:
您存储在列表中的对象使用的静态字段
不小心将相同的对象添加到列表中
如果列表中的对象将数据存储在静态字段中,则列表中的每个对象看起来都是相同的,因为它们具有相同的值。考虑下面的类:
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。 (参见"Understanding Class Members" 教程。)
如果您使用下面的代码将多个 Foo 对象添加到列表中,每个实例将从对 getValue() 的调用中返回 3:
for (int i = 0; i < 4; i++) {
list.add(new Foo(i));
}
解决方案很简单 - 不要对类中的字段使用 static 关键字,除非您确实希望在该类的每个实例之间共享值。
如果将临时变量添加到列表中,则必须在每次循环时创建要添加的对象的新实例。考虑以下错误代码sn-p:
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);
}
【讨论】:
tmp)被添加到列表中三次。该对象的值为 2,因为在循环的最后一次迭代中调用了 tmp.setValue(2)。
Adding the same object 部分中的you must create a new instance each time you loop 部分:请注意,引用的实例涉及您要添加的对象,而不是您要添加的对象。
您的问题在于 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中的一个变量,用来检查上面sn -p的有效性;你可以根据你的用例有一些其他的实现。
【讨论】:
static”是什么意思?对你来说什么是非静态类?
public class SomeClass{/*some code*/} 和静态:public static class SomeStaticClass{/*some code*/}。我希望现在更清楚了。
static 类在 java 中不存在,您可以将 static 添加到嵌套类中,但这不会自动使其成为单个实例/相同引用...
日历实例也有同样的问题。
错误代码:
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);
}
【讨论】:
Calendar。
每次将对象添加到 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);
}
【讨论】: