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

标签 java

我正在向 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。 (请参阅 "Understanding Class Members" 教程。)

如果使用下面的代码将多个 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);
}

关于java - 为什么我的 ArrayList 包含添加到列表中的最后一项的 N 个副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36627044/

相关文章:

java - 与非 volatile 写入相比, volatile 写入的成本增加

java - 从 HashMap 中返回特定值

java - Spring 与 Selenium 的依赖集成

java - 比较 Composer 名称,或者如何找到两个字符串之间的 'close enough' 匹配项?

java - 如何使用 NIO 创建当前工作目录上一级文件夹的 zip 文件?

java - 字符串列表中单词的频率

java - java集合框架中的比较器

java - 如何在Fragment Activity中设置Java代码

java - 单元测试验证失败

java - 查找随机数组的中值