programing

수집을 통한 반복으로 동시 수정 회피루프에서 개체를 제거할 때 예외 발생

sourcetip 2022. 7. 16. 09:01
반응형

수집을 통한 반복으로 동시 수정 회피루프에서 개체를 제거할 때 예외 발생

, 다요, 다 하다 하다 하다 못 거 있잖아요.ConcurrentModificationException:

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

하지만 이것은 때때로 효과가 있는 것 같지만 항상 그렇지는 않다.구체적인 코드는 다음과 같습니다.

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

그 결과 다음과 같은 결과가 초래됩니다.

Exception in thread "main" java.util.ConcurrentModificationException

비록 여러 개의 스레드가 작동하지 않지만요.어쨌든.

이 문제에 대한 가장 좋은 해결책은 무엇입니까?이 예외를 발생시키지 않고 루프 상태에서 컬렉션에서 항목을 제거하려면 어떻게 해야 합니까?

, 임의의 것을 .Collection꼭 여,는 아니지만ArrayList수 없습니다.get.

Iterator.remove() 다음과 같이 사용할 수 있습니다.

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

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

반복 중에 컬렉션을 변경할 수 있는 유일한 안전한 방법입니다.반복 중에 기본 컬렉션이 다른 방법으로 변경되면 동작이 지정되지 않습니다.

출처 : docs.oracle > 수집 인터페이스


마찬가지로 '이렇게'가 '이렇게'가 있어요.ListIterator아이템을 추가하고 싶은 경우는, 를 사용할 수 있습니다.Iterator#remove-이것을 가능하게 설계되어 있습니다.


삭제하려고 하면 됩니다.putMap그 내용을 반복하면서.

이 방법은 다음과 같습니다.

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

」는, 「 」는 「 」로 되어 있습니다만, 「 」는 「 」로..remove()★★★★★★★★★★★★★★★★★★.

Java 8에서는 새로운 방법을 사용할 수 있습니다.예에 적용됨:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

이 되어 즉,의 삭제 하는 것이기 에, 에러가 내용에 하겠습니다."java.util.ConcurrentModificationException"집니니다

하고 Iterator의 .next(),remove() ★★★★★★★★★★★★★★★★★」hasNext().

다음 암호는 이렇게 생겼는데...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

에서는 방법 「」을 .checkForComodification로서 실장되다

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

보시다시피 컬렉션에서 요소를 명시적으로 삭제하려고 하면 됩니다.으로 「」가 된다.modCount expectedModCountConcurrentModificationException.

앞서 설명한 대로 반복기를 직접 사용하거나 두 번째 컬렉션을 유지하고 제거할 각 항목을 새 컬렉션에 추가한 후 마지막에 모두 제거할 수 있습니다.이것에 의해, 메모리 사용량과 CPU 시간의 증가에 수반해, 각 루프의 타입의 안전성을 계속 사용할 수 있습니다(대단히 큰 리스트나 낡은 컴퓨터가 없는 한, 큰 문제는 없습니다).

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}

이러한 경우, 일반적인 방법은 뒤로 가는 것입니다(was?).

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

Java에서 더 할 수 있게 Java 8에서 더 나은 방법을 사용할 수 있습니다).removeIf ★★★★★★★★★★★★★★★★★」filter시냇가에서.

for 루프가 있는 Claudius와 같은 대답:

for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
    Object object = it.next();
    if (test) {
        it.remove();
    }
}

Eclipse Collections를 사용하는 방법removeIfMutable Collection에 정의된 기능은 다음과 같습니다.

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

Java 8 Lambda 구문에서는 다음과 같이 기술할 수 있습니다.

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

의 콜Predicates.cast()는 디폴트 "Default "Default "Default "Default "Default "Default "Default "Default "는 필수입니다.removeIf되었습니다.java.util.Collection인터페이스를 지정합니다.

주의: 저는 Eclipse Collections의 커밋입니다.

기존 목록의 복사본을 만들고 새 복사본에 대해 반복합니다.

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}

사람들은 포어치 루프에 의해 반복되고 있는 컬렉션에서 삭제할 없다고 주장하고 있다.저는 단지 이것이 기술적으로 잘못되었다는 것을 지적하고 정확하게 설명하고자 합니다(OP의 질문은 이 가정에 대해 알 필요가 없을 정도로 진보되어 있습니다).

for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
    if (obj.isTouched()) {
        untouchedSet.remove(obj);
        touchedSt.add(obj);
        break;  // this is key to avoiding returning to the foreach
    }
}

것은 Colletion일단 반복을 계속하면 안 된다는 거죠.에, 「」는break를 참조해 주세요.

이 답변이 다소 전문적인 사용 사례이며 제가 도착한 원래 스레드에 더 적합하다면 죄송합니다. 이 답변은 중복으로 표시되며 잠겨 있습니다(이 스레드는 더 미묘한 차이가 있지만).

기존 For 루프 사용

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}

ConcurrentHashMap, ConcurrentLinkedQueue 또는 ConcurrentSkipListMap은 ConcurrentModification을 슬로우하지 않기 때문에 다른 옵션일 수 있습니다.항목을 제거하거나 추가하는 경우에도 예외입니다.

또 다른 방법은 arrayList 복사본을 반복용으로만 사용하는 것입니다.

List<Object> l = ...
    
List<Object> iterationList = ImmutableList.copyOf(l);
    
for (Object curr : iterationList) {
    if (condition(curr)) {
        l.remove(curr);
    }
}

A ListIterator를 사용하면 목록 내의 항목을 추가하거나 제거할 수 있습니다.를 들어, 여러분이 리스트가 .Car★★★★★★★★★★★★★★★★★★:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}

Java 8에 관한 질문이 너무 오래된 것은 알지만 Java 8을 사용하는 사용자는 removeIf()를 쉽게 사용할 수 있습니다.

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);

이제 다음 코드를 사용하여 제거할 수 있습니다.

l.removeIf(current -> current == 5);

Java 동시 수정 예외

  1. 단일 스레드
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        list.remove(it.next()); //throws ConcurrentModificationException
    }
}

: ★★★★★★★★★★★★★★★★★★★★★★★★」remove()

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        it.remove()
    }
}
  1. 멀티 스레드
  • 다른 컬렉션에 대해 복사/복사 및 반복.소량 컬렉션용
  • synchronize[대략]
  • 스레드 세이프 컬렉션[대략]

위의 문제에 대한 제안이 있습니다.보조 목록이나 추가 시간이 필요하지 않습니다.같은 일을 다른 방법으로 할 수 있는 예를 찾아주세요.

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

이렇게 하면 동시성 예외가 방지됩니다.

for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

internal iterator.next() 콜을 건너뛰면 캐치는 목록에서 요소를 삭제한 후가 됩니다.아직 작동해!이렇게 코드를 쓰는 것은 제안하지 않지만, 그 이면에 있는 개념을 이해하는 것은 도움이 됩니다:-)

건배!

스레드 세이프 컬렉션 수정 예:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}

하나의 해결책은 목록을 회전하고 첫 번째 요소를 삭제하여 Concurrent Modification을 피하는 것입니다.예외 또는 IndexOutOfBoundsException

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

이 옵션을 사용해 보십시오(목록에서 동일한 요소를 모두 제거함i):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}

while loop을 사용할 수 있습니다.

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if(entry.getKey().equals("test")) {
        iterator.remove();
    } 
}

결국 이렇게 됐어요.ConcurrentModificationException를 사용하여 목록을 반복할 때stream().map()방법.하지만, 그for(:)목록을 반복 및 수정하는 동안 예외가 발생하지 않았습니다.

여기 코드 스니펫이 있습니다.누군가에게 도움이 되는 경우:여기서 반복하고 있는 것은ArrayList<BuildEntity>목록.remove(obj)를 사용하여 변경합니다.

 for(BuildEntity build : uniqueBuildEntities){
            if(build!=null){
                if(isBuildCrashedWithErrors(build)){
                    log.info("The following build crashed with errors ,  will not be persisted -> \n{}"
                            ,build.getBuildUrl());
                    uniqueBuildEntities.remove(build);
                    if (uniqueBuildEntities.isEmpty()) return  EMPTY_LIST;
                }
            }
        }
        if(uniqueBuildEntities.size()>0) {
            dbEntries.addAll(uniqueBuildEntities);
        }

HashMap을 사용하는 경우 Java(8+)의 새로운 버전에서는 다음 3가지 옵션을 각각 선택할 수 있습니다.

public class UserProfileEntity {
    private String Code;
    private String mobileNumber;
    private LocalDateTime inputDT;
    // getters and setters here
}
HashMap<String, UserProfileEntity> upMap = new HashMap<>();


// remove by value
upMap.values().removeIf(value -> !value.getCode().contains("0005"));

// remove by key
upMap.keySet().removeIf(key -> key.contentEquals("testUser"));

// remove by entry / key + value
upMap.entrySet().removeIf(entry -> (entry.getKey().endsWith("admin") || entry.getValue().getInputDT().isBefore(LocalDateTime.now().minusMinutes(3)));

가장 좋은 방법(권장)은java.util.concurrent패키지.이 패키지를 사용하면 이 예외를 쉽게 피할 수 있습니다.수정된 코드 참조:

public static void main(String[] args) {
    Collection<Integer> l = new CopyOnWriteArrayList<Integer>();
    
    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }
    
    for (Integer i : l) {
        if (i.intValue() == 5) {
            l.remove(i);
        }
    }
    
    System.out.println(l);
}

ArrayList:remove(int index)- if(index가 마지막 요소의 위치)인 경우, 이 명령어를 사용하지 않고 회피합니다.System.arraycopy()시간이 걸리지 않습니다.

(인덱스가 감소하는 경우) 어레이 복사 시간이 증가합니다.그런데 목록 요소도 감소합니다!

방법은 입니다.while(list.size()>0)list.remove(list.size()-1);1 //O(1)를 while(list.size()>0)list.remove(0);n)//O(n)/는 O(factor(n)//.

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • 인덱스 루프: 1090 밀리초
  • desc index의 경우: 519밀리초-최고
  • 반복기용: 1043 밀리초

이 질문이 '어느 정도'로 하고 있다는 것을 있습니다.Collection으로는 「」를 참조해 주세요.List. 그렇지만 그들이 참으로 목록 기준과 작업하고 있다고 이 질문 읽기 위해 당신 ConcurrentModification을 피할 수 있다.한 while-loop(반면 내에 수정)과 예외 대신 만약 당신이 Iterator(역시 만약 당신이 일반적으로 또는 구체적으로 반복되는 주문start-to-end 각 요소에 정차하는 다른를 달성하려면 피하그것을 피하고 싶어 하는 경우에는 나는 믿는 유일한 주문을 피하고 싶다.Iterator : : : : :)))) :

* 갱신:아래의 코멘트를 참조해, 종래의 루프 방식에서도 유사한 것을 명확하게 할 수 있습니다.

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

동시 수정 없음그 코드에서는 예외입니다.

루프가 처음부터 시작되지 않고 모든 요소에서 멈추지 않는 것을 알 수 있습니다.Iterator할 수 없습니다).

, 도 표시됩니다.get을 받다list참조가 단순한 경우 수행될 수 없습니다.Collection 더 구체적인 것 중 )List -type of -type of -templicateCollection) -List에는 「Interface(인터페이스)」가 포함됩니다.getCollection인터페이스에는 없습니다., 그 그 차이가 것이다.list가 될 수 .Collection[따라서 기술적으로 이 답변은 접선식 답변이 아니라 직접 답변이 됩니다]

와 같은 의 정지 합니다(FWIWW와 로).Iterator( ) :

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}

재귀도 사용할 수 있습니다.

Java에서의 재귀는 메서드가 지속적으로 자신을 호출하는 프로세스입니다.Java에서 자신을 호출하는 메서드를 재귀 메서드라고 합니다.

이것이 최선의 방법은 아닐 수 있지만, 대부분의 작은 경우에는 이 방법이 허용됩니다.

"두 번째 빈 어레이를 만들고 보관할 어레이만 추가"

어디서 읽었는지 기억이 안 나는데...누군가가 찾기를 바라거나 내가 받을 자격이 없는 평판을 얻지 않기 위해 이 위키를 만들 것이다.

언급URL : https://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re

반응형