ProgramingTip

내 ArrayList에 목록에 마지막으로 추가 된 항목의 N 개의 복사본이 포함 된 이유는 무엇입니까?

bestdevel 2020. 10. 29. 08:24
반응형

내 ArrayList에 목록에 마지막으로 추가 된 항목의 N 개의 복사본이 포함 된 이유는 무엇입니까?


ArrayList에 세 개의 다른 개체를 추가하고 추가 할 수 있도록 마지막으로 추가 한 개체의 복사본 세 개가되어 있습니다.

예를 들면 :

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

예상 :

0
1
2

실제 :

2
2
2

내가 어떤 실수를 했습니까?

참고 :이 사이트에서 발생하는 많은 문제에 대한 표준 Q & A로 설계되었습니다.


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

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

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

정적 필드

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

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모든 인스턴스간에 공유되는 하나만 있습니다. ( "클래스 멤버 이해" 안내를 참조하십시오 .)Foostatic

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

... 여기서 tmpobject-는 루프 외부에서 생성되었습니다. 결과적으로 번 인스턴스 개체 가 목록에 세 추가됩니다. 인스턴스는 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);
}

문제는 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위 코드 조각의 유효성을 확인 하는 변수입니다 . 사례에 따라 구현을 사용합니다.


동일한 인스턴스에서 동일한 문제가 발생했습니다.

잘못된 코드 :

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에 개체를 추가 할 때마다 새 개체를 추가하고 아직 사용하지 않은 개체를 추가해야합니다. 무슨 일이 일어나고 있는지 동일한 객체 사본을 추가하면 동일한 객체가 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));

참고 URL : https://stackoverflow.com/questions/19843506/why-does-my-arraylist-contain-n-copies-of-the-last-item-added-to-the-list

반응형