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/40132040/

相关文章:

java - 检查主java类中面板的事件

java - URL 是否必须以/或不在 Java 的 URL 类内部结尾?

java - 在android中绘制路径?

java - 自定义带有底栏的android布局?

Java BigDecimal : Round to the nearest whole value

java - GlassFish 5 上的 JNDI 查找失败

带有 GUI 的 Javax.swing.timer (Eclipse)

java - 将 axis2 中的 Web 服务客户端放入 tomcat 中的 Maven Web 应用程序中

java - JDBC SQLite 不强制唯一主键约束

java - 如何防止用户在注销应用程序后访问历史记录