Java 为什么我的ArrayList包含添加到列表中的最后一项的N个副本?

Java 为什么我的ArrayList包含添加到列表中的最后一项的N个副本?,java,list,arraylist,static,Java,List,Arraylist,Static,我正在向ArrayList添加三个不同的对象,但该列表包含我添加的最后一个对象的三个副本 例如: for (Foo f : list) { System.out.println(f.getValue()); } 预期: 0 1 2 实际: 2 2 2 我犯了什么错误 注:这是针对本网站上出现的许多类似问题的规范问答。此问题有两个典型原因: 存储在列表中的对象使用的静态字段 意外地将同一对象添加到列表中 静态场 如果列表中的对象将数据存储在静态字段中,则列表中的每个对象都

我正在向ArrayList添加三个不同的对象,但该列表包含我添加的最后一个对象的三个副本

例如:

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值
,它在
Foo
的所有实例之间共享,因为它被声明为
static
。(请参见教程。)

如果使用下面的代码将多个
Foo
对象添加到列表中,则每个实例将从调用
getValue()
返回
3

这里,
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);
}
List List=new ArrayList();
对于(int i=0;i<3;i++){

Foo tmp=new Foo();//您的问题在于类型
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 objects=new ArrayList();
for(int i=0;i
而不是:

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
}
List objects=new ArrayList();
SomeStaticClass myStaticObject=新的SomeStaticClass();
for(int i=0;i

这里,
tag
SomeStaticClass
中的一个变量,用于检查上述代码段的有效性;您可以根据您的用例使用其他一些实现。

每次向ArrayList添加对象时,请确保您添加了一个新的对象而不是尚未使用的对象。当您添加相同的1个同一个对象被添加到ArrayList中的不同位置。当您对一个对象进行更改时,因为同一个副本被一次又一次地添加,所以所有副本都会受到影响。 例如 假设您有这样一个ArrayList:

ArrayList<Card> list = new ArrayList<Card>();
Card c = new 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张卡副本,因此在添加新对象时,您可以执行以下操作:

list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));

日历实例也有同样的问题

错误代码:

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);
}

它还可能导致使用相同的引用而不是使用新的引用

 List<Foo> list = new ArrayList<Foo>();        

 setdata();
......

public void setdata(int i) {
  Foo temp = new Foo();
  tmp.setValue(i);
  list.add(tmp);
}
List List=new ArrayList();
setdata();
......
公共无效设置数据(int i){
Foo temp=新的Foo();
tmp.设定值(i);
添加列表(tmp);
}
而不是:

List<Foo> list = new ArrayList<Foo>(); 
Foo temp = new Foo();       
setdata();
......

public void setdata(int i) {
  tmp.setValue(i);
  list.add(tmp);
} 
List List=new ArrayList();
Foo temp=新的Foo();
setdata();
......
公共无效设置数据(int i){
tmp.设定值(i);
添加列表(tmp);
} 

hello@Duncan nice solution,我想问你,在“添加同一对象”中,为什么三个不同的实例会包含值2,不是所有三个实例都应该包含3个不同的值吗?希望你能尽快回复,thanks@Dev因为同一个对象(
tmp
)被添加到列表中三次。由于调用
tmp.setValue(2),该对象的值为2
在循环的最后一次迭代中。好吧,这里的问题是因为同一个对象,如果我在数组列表中添加同一个对象三次,arraylist obj的所有三个位置都将引用同一个对象,是吗?@Dev-Yup,确实如此。我最初忽略了一个明显的事实:关于
部分,每次都必须创建一个新实例ou loop
在添加相同对象一节中:请注意,引用的实例涉及的是您正在添加的对象,而不是您正在添加的对象。您所说的“type
static
”是什么意思?对您来说什么是非静态类?例如,非静态:
公共类SomeClass{/*somecode*/}
&static:
公共静态类SomeStaticClass{/*somecode*/}
。我希望现在更清楚。因为,一个静态类的所有对象共享相同的地址,如果它们在循环中初始化,并且在每次迭代中使用不同的值进行设置。所有这些对象最终都将具有相同的值,该值将等于上一次迭代的值或修改最后一个对象时的值。我希望现在更清楚。为了将来读者:你根本不应该使用日历。
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);

}
 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);
}