ArrayListに3つの異なるオブジェクトを追加していますが、リストには最後に追加したオブジェクトの3つのコピーが含まれています。
例えば:
for (Foo f : list) {
System.out.println(f.getValue());
}
期待:
0
1
2
実際:
2
2
2
私はどんな間違いをしましたか?
注:これは、このサイトで発生する多くの同様の問題に対する標準的なQ&Aとなるように設計されています。
回答:
この問題には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 value
、のすべてのインスタンス間で共有されるのは1つだけです。(「クラスメンバーについて」チュートリアルを参照してください。)Foo
static
Foo
以下のコードを使用してリストに複数のオブジェクトを追加すると、各インスタンスは次3
の呼び出しから戻りますgetValue()
。
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
オブジェクトはループの外側に構築されています。その結果、同じオブジェクトインスタンスがリストに3回追加されています。の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
)がリストに3回追加されたため。そしてtmp.setValue(2)
、ループの最後の反復での呼び出しのため、そのオブジェクトの値は2です。
you must create a new instance each time you loop
セクションの部分についてAdding the same object
:参照されているインスタンスは、追加しているオブジェクトではなく、追加しているオブジェクトに関係していることに注意してください。
あなたの問題は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
上記のスニペットの有効性を確認するためには、ユースケースに基づいて、他の実装を行うことができます。
static
」とはどういう意味ですか?あなたにとって非静的クラスは何でしょうか?
public class SomeClass{/*some code*/}
&静的:public static class SomeStaticClass{/*some 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);
}
カレンダーの新しいオブジェクトを作成する必要があります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);
}
ArrayListにオブジェクトを追加するたびに、まだ使用されていない新しいオブジェクトを追加してください。何が起こっているのかというと、同じ1つのオブジェクトのコピーを追加すると、その同じオブジェクトがArrayListの異なる位置に追加されます。また、1つに変更を加えると、同じコピーが何度も追加されるため、すべてのコピーが影響を受けます。たとえば、次のようなArrayListがあるとします。
ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();
このカードcをリストに追加すれば、問題なく追加されます。場所0に保存されます。ただし、同じカードcをリストに保存すると、場所1に保存されます。したがって、同じ1つのオブジェクトをリストの2つの異なる場所に追加したことを忘れないでください。ここで、そのCardオブジェクトcを変更すると、場所0と1のリスト内のオブジェクトも同じオブジェクトであるため、その変更が反映されます。
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つあるとすると、新しいオブジェクトを追加するときに、次のことができます。
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);
}