Program Tip

목록 반복 중에 java.util.List에서 요소를 제거 할 때 ConcurrentModificationException이 발생합니까?

programtip 2020. 12. 3. 19:07
반응형

목록 반복 중에 java.util.List에서 요소를 제거 할 때 ConcurrentModificationException이 발생합니까?


@Test
public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }

    for(String st:li){
        if(st.equalsIgnoreCase("str3"))
            li.remove("str3");
    }
    System.out.println(li);
}

이 코드를 실행하면 ConcurrentModificationException이 발생합니다.

목록에서 지정된 요소를 제거하면 목록의 크기가 변경된 것을 알지 못하는 것처럼 보입니다.

이것이 컬렉션 및 요소 제거와 관련된 일반적인 문제인지 궁금합니다.


이것이 반복하는 동안 컬렉션에서 요소를 제거 할 수 있는 Iterator.remove () 메서드 의 목적이라고 생각합니다 .

예를 들면 :

Iterator<String> iter = li.iterator();
while(iter.hasNext()){
    if(iter.next().equalsIgnoreCase("str3"))
        iter.remove();
}

반복자없이 목록에서 제거하는 Java 8 방법은 다음과 같습니다.

li.removeIf(<predicate>)

List<String> li = new ArrayList<String>();
// ...
li.removeIf(st -> !st.equalsIgnoreCase("str3"));

이 예외가 항상 다른 스레드에 의해 객체가 동시에 수정되었음을 나타내는 것은 아닙니다. 단일 스레드가 개체의 계약을 위반하는 일련의 메서드 호출을 발행하면 개체가이 예외를 throw 할 수 있습니다. 예를 들어, 스레드가 fail-fast iterator를 사용하여 컬렉션을 반복하는 동안 컬렉션을 직접 수정하는 경우 반복자는이 예외를 처리합니다.

http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html 에서 가져옴


예, 사람들은 그것에 부딪칩니다. 문제는 목록을 반복하는 동안 목록을 수정할 수 없다는 것입니다. 나는 과거에 두 가지 대안을 사용했습니다.

  1. 제거 할 항목의 인덱스를 추적 한 다음 반복을 완료 한 후 제거 할 수 있습니다.
  2. 또는 반복하면서 유지하려는 모든 항목을 새 목록에 복사 한 다음 완료되면 이전 목록을 삭제할 수 있습니다.

이러한 옵션은 제거 할 요소를 찾기 위해 목록을 반복해야한다고 가정합니다. 목록 요소가 테스트 할 수있는 속성이있는 복잡한 개체 인 경우 유용합니다.

특별한 경우에는 removeAll을 사용할 수 있으므로 반복 할 필요조차 없습니다. 여기 에서 API를 보십시오 . 인수에 포함되지 않은 모든 것을 버리는 retainAll과 같은 멋진 메소드도 있습니다. 목록의 객체가 같고 해시 코드를 올바르게 구현할 때마다 제거 / 유지 유사 메서드를 사용할 수 있습니다. 앱의 인스턴스 간 동등성을 식별하기 위해 equals / hashcode에 의존 할 수없는 경우 직접 제거해야합니다 ....


Java 8 버전을 언급 할 가치가 있다고 생각합니다.

@Test
public void testListCur() {
    List<String> li = new ArrayList<String>();
    for (int i = 0; i < 10; i++) {
        li.add("str" + i);
    }

    li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList());

    System.out.println(li);
}

for-each 루프에서 직접 요소를 제거하려는 목록의 복사본을 만들 수 있습니다. 저에게는 이것이 가장 간단한 방법입니다. 이 같은:

for (String stringIter : new ArrayList<String>(myList)) {
    myList.remove(itemToRemove);
}

도움이 되길 바랍니다 ..


이것을 시도하십시오 (Java 8) :

list.removeIf(condition);

나는이 문제가 있었고 더 쉬운 방법은 hvgotcodes가 준 두 번째 방법과 동일하다고 생각합니다.

또는 반복하면서 유지하려는 모든 항목을 새 목록에 복사 한 다음 완료되면 이전 목록을 삭제할 수 있습니다.

@Test
public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }
    List<String> finalLi = new ArrayList<String>();
    for(String st:li){
        if(st.equalsIgnoreCase("str3")){
            // Do nothing
        } else {
            finalLi.add(st);
        }
    }
    System.out.println(finalLi);
}

ArrayList에 필드가 있음 modCount-컬렉션 수정 횟수

메소드를 호출 iterator()하면 새 객체가 생성 Itr됩니다. 필드가 있습니다 expectedModCount. expectedModCount필드는 modCount값으로 초기화됩니다 . 호출 할 때

li.remove("str3");

modCount증분. 언제 li반복자 통해 액세스를 시도합니까?expectedModCount == modCount

그리고 그것이 거짓 던지면 ConcurrentModificationException

Hence if you get iterator and after collection modified - iterator is considered not valid and you cannot use it.


I looped a different way...

public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }

    for(int i=0; i<li.size(); i++)
        if(li.get(i).equalsIgnoreCase("str3"))
            li.remove(i--);

    System.out.println(li);
}

I think that best answer is from bigdev.de, but i would like to add something to it(like if the item is removed from a list, maybe you would like to log that somewhere or something):

List<String> list = new ArrayList<>();

list.removeIf(a -> {
                boolean condition = a.equalsIgnoreCase("some condition");
                if(condition)
                    logger.info("Item removed from the list: " + a);
                return condition;
  });

참고URL : https://stackoverflow.com/questions/5113016/getting-a-concurrentmodificationexception-thrown-when-removing-an-element-from-a

반응형