Java 为什么我的 ArrayList 包含添加到列表中的最后一项的 N 个副本?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19843506/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does my ArrayList contain N copies of the last item added to the list?
提问by Duncan Jones
I'm adding three different objects to an ArrayList, but the list contains three copies of the last object I added.
我正在向 ArrayList 添加三个不同的对象,但该列表包含我添加的最后一个对象的三个副本。
For example:
例如:
for (Foo f : list) {
System.out.println(f.getValue());
}
Expected:
预期的:
0
1
2
Actual:
实际的:
2
2
2
What mistake have I made?
我犯了什么错误?
Note: this is designed to be a canonical Q&A for the numerous similar issues that arise on this site.
注意:这是针对本网站上出现的众多类似问题的规范问答。
采纳答案by Duncan Jones
This problem has two typical causes:
这个问题有两个典型的原因:
Static fields used by the objects you stored in the list
Accidentally adding the sameobject to the list
您存储在列表中的对象使用的静态字段
不小心将相同的对象添加到列表中
Static Fields
静态场
If the objects in your list store data in static fields, each object in your list will appear to be the same because they hold the same values. Consider the class below:
如果列表中的对象将数据存储在静态字段中,则列表中的每个对象看起来都相同,因为它们具有相同的值。考虑下面的类:
public class Foo {
private static int value;
// ^^^^^^------------ - Here's the problem!
public Foo(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
In that example, there is only one int value
which is shared between all instances of Foo
because it is declared static
. (See "Understanding Class Members"tutorial.)
在那个例子中,只有一个int value
在 的所有实例之间共享,Foo
因为它被声明为static
。(请参阅“了解班级成员”教程。)
If you add multiple Foo
objects to a list using the code below, each instance will return 3
from a call to getValue()
:
如果您Foo
使用以下代码将多个对象添加到列表中,则每个实例将从3
对 的调用返回getValue()
:
for (int i = 0; i < 4; i++) {
list.add(new Foo(i));
}
The solution is simple - don't use the static
keywords for fields in your class unless you actually want the values shared between every instance of that class.
解决方案很简单 - 不要static
在类中的字段中使用关键字,除非您确实希望在该类的每个实例之间共享值。
Adding the Same Object
添加相同的对象
If you add a temporary variable to a list, you must create a new instance of the object you are adding, each time you loop. Consider the following erroneous code snippet:
如果将临时变量添加到列表中,则每次循环时都必须创建要添加的对象的新实例。考虑以下错误的代码片段:
List<Foo> list = new ArrayList<Foo>();
Foo tmp = new Foo();
for (int i = 0; i < 3; i++) {
tmp.setValue(i);
list.add(tmp);
}
Here, the tmp
object was constructed outside the loop. As a result, the same object instanceis being added to the list three times. The instance will hold the value 2
, because that was the value passed during the last call to setValue()
.
在这里,tmp
对象是在循环之外构造的。结果,同一个对象实例被添加到列表中 3 次。该实例将保存该值2
,因为这是在上次调用setValue()
.
To fix this, just move the object construction inside the loop:
要解决这个问题,只需在循环内移动对象构造:
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);
}
回答by Shashank
Your problem is with the type static
which requires a new initialization every time a loop is iterated. If you are in a loop it is better to keep the concrete initialization inside the 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);
}
Instead of:
代替:
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
}
Here tag
is a variable in SomeStaticClass
to check the validity of the above snippet; you can have some other implementation based on your use case.
这tag
是SomeStaticClass
检查上述代码片段有效性的变量;您可以根据您的用例进行其他一些实现。
回答by Faraz
Every time you add an object to an ArrayList, make sure you add a new object and not already used object. What is happening is that when you add the same 1 copy of object, that same object is added to different positions in an ArrayList. And when you make change to one, because the same copy is added over and over again, all the copies get affected. For example, Say you have an ArrayList like this:
每次向 ArrayList 添加对象时,请确保添加新对象而不是已使用的对象。发生的情况是,当您添加相同的 1 个对象副本时,该对象会被添加到 ArrayList 中的不同位置。当您对一个进行更改时,因为一遍又一遍地添加相同的副本,所有副本都会受到影响。例如,假设您有一个像这样的 ArrayList:
ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();
Now if you add this Card c to list, it will be added no problem. It will be saved at location 0. But, when you save the same Card c in the list, it will be saved at location 1. So remember that you added same 1 object to two different locations in a list. Now if you make a change that Card object c, the objects in a list at location 0 and 1 will also reflect that change, because they are the same object.
现在,如果您将此 Card c 添加到列表中,则添加没有问题。它将保存在位置 0。但是,当您在列表中保存相同的 Card c 时,它将保存在位置 1。因此请记住,您将相同的 1 对象添加到列表中的两个不同位置。现在,如果您更改 Card 对象 c,位置 0 和 1 处的列表中的对象也将反映该更改,因为它们是同一个对象。
One solution would be to make a constructor in Card class, that accepts another Card object. Then in that constructor, you can set the properties like this:
一种解决方案是在 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
}
And lets say you have the same 1 copy of Card, so at the time of adding a new object, you can do this:
假设您有相同的 1 个 Card 副本,因此在添加新对象时,您可以执行以下操作:
list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));
回答by basti12354
Had the same trouble with the calendar instance.
日历实例也有同样的问题。
Wrong code:
错误代码:
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);
}
You have to create a NEW object of the calendar, which can be done with calendar.clone()
;
你必须创建一个新的日历对象,这可以用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);
}
回答by Shaikh Mohib
It can also consequence of using the same reference instead of using a new one.
它也可能是使用相同引用而不是使用新引用的结果。
List<Foo> list = new ArrayList<Foo>();
setdata();
......
public void setdata(int i) {
Foo temp = new Foo();
tmp.setValue(i);
list.add(tmp);
}
Instead of:
代替:
List<Foo> list = new ArrayList<Foo>();
Foo temp = new Foo();
setdata();
......
public void setdata(int i) {
tmp.setValue(i);
list.add(tmp);
}