내 ArrayList에 목록에 추가 된 마지막 항목의 N 개 사본이 포함되는 이유

java list arraylist static


3 개의 다른 객체를 ArrayList에 추가하고 있지만 목록에 마지막으로 추가 한 3 개의 객체가 포함되어 있습니다.

예를 들면 다음과 같습니다.

for (Foo f : list) {
  System.out.println(f.getValue());
}    

Expected:

0
1
2

Actual:

2
2
2

내가 무슨 실수를 한거야?

참고 :이 사이트에서 발생하는 수많은 유사한 문제에 대한 정식 Q & A가되도록 설계되었습니다.




Answer 1 Duncan Jones


이 문제에는 두 가지 일반적인 원인이 있습니다.

  • 목록에 저장 한 객체가 사용하는 정적 필드

  • 실수로 동일한 객체를 목록에 추가

정적 필드

목록의 개체가 정적 필드에 데이터를 저장하는 경우 목록의 각 개체는 동일한 값을 가지므로 동일하게 나타납니다. 아래 클래스를 고려하십시오.

public class Foo {
  private static int value; 
  //      ^^^^^^------------ - Here's the problem!

  public Foo(int value) {
    this.value = value;
  }

  public int getValue() {
    return value;
  }
}

이 예에서는 Foostatic 으로 선언되어 있기 때문에 Foo의 모든 인스턴스간에 공유되는 하나의 int value 있습니다. ( "반원 이해하기" 튜토리얼을 참조하십시오 .)

아래 코드를 사용하여 여러 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 객체는 루프 외부에서 구성되었습니다. 결과적으로 동일한 객체 인스턴스 가 목록에 3 번 추가됩니다. setValue() 를 마지막으로 호출하는 동안 전달 된 값이므로 인스턴스는 값 2 를 보유합니다 .

이 문제를 해결하려면 루프 내에서 객체 구성을 이동하십시오.

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



Answer 2 Shashank


문제는 루프 유형이 반복 될 때마다 새로운 초기화가 필요한 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
}

여기 tagSomeStaticClass 의 변수로 위 코드 조각의 유효성을 검사합니다. 사용 사례에 따라 다른 구현을 할 수 있습니다.




Answer 3 basti12354


캘린더 인스턴스와 동일한 문제가있었습니다.

잘못된 코드:

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

}



Answer 4 Faraz


ArrayList에 객체를 추가 할 때마다 아직 사용하지 않은 새 객체를 추가해야합니다. 일어나고있는 일은 동일한 객체 사본 1 개를 추가하면 해당 객체가 ArrayList의 다른 위치에 추가된다는 것입니다. 하나를 변경하면 동일한 사본이 반복해서 추가되므로 모든 사본이 영향을받습니다. 예를 들어 다음과 같이 ArrayList가 있다고 가정하십시오.

ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();

이제이 카드 c를 목록에 추가해도 아무런 문제가 없습니다. 위치 0에 저장됩니다. 그러나 동일한 카드 c를 목록에 저장하면 위치 1에 저장됩니다. 따라서 목록에서 두 개의 다른 위치에 동일한 1 개의 객체를 추가했음을 기억하십시오. 이제 Card 객체 c를 변경하면 위치 0과 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));



Answer 5 Shaikh Mohib


새 참조를 사용하는 대신 동일한 참조를 사용하면 결과가 발생할 수 있습니다.

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