programing

반복하는 동안 HashSet에서 요소 제거

copysource 2022. 9. 25. 14:57
반응형

반복하는 동안 HashSet에서 요소 제거

따라서 반복 중에 Java HashSet에서 요소를 제거하려고 하면 Concurrent Modification이 나타납니다.예외입니다.다음 예시와 같이 HashSet에서 요소의 서브셋을 삭제하는 가장 좋은 방법은 무엇입니까?

Set<Integer> set = new HashSet<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

// Throws ConcurrentModificationException
for(Integer element : set)
    if(element % 2 == 0)
        set.remove(element);

여기 해결책이 있습니다만, 그다지 우아하다고는 생각하지 않습니다.

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

for(Integer element : set)
    if(element % 2 == 0)
        removeCandidates.add(element);

set.removeAll(removeCandidates);

감사합니다!

세트의 요소에 대해 수동으로 반복할 수 있습니다.

Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    if (element % 2 == 0) {
        iterator.remove();
    }
}

이 패턴은 자주 볼 수 있습니다.for가 아닌 루프while루프:

for (Iterator<Integer> i = set.iterator(); i.hasNext();) {
    Integer element = i.next();
    if (element % 2 == 0) {
        i.remove();
    }
}

사람들이 지적한 바와 같이,for루프가 권장되는 이유는 반복 변수(i이 경우)는 더 작은 범위로 제한됩니다.

취득하는 이유ConcurrentModificationException엔트리가 Iterator.remove()가 아닌 Set.remove()를 통해 삭제되기 때문입니다.반복 중에 Set.remove()통해 엔트리가 삭제되면 Concurrent Modification이 나타납니다.예외.한편, 이 경우 반복 중에 Iterator.remove()를 통한 엔트리 삭제가 지원됩니다.

새로운 for 루프는 좋지만 안타깝게도 이 경우 반복 참조를 사용할 수 없기 때문에 작동하지 않습니다.

반복 중에 항목을 제거해야 하는 경우 반복기를 직접 사용하는 긴 형식을 사용해야 합니다.

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}

Java 8 Collection에는 removeIf라고 불리는 좋은 방법이 있어 더 쉽고 안전하게 만들 수 있습니다.API 문서에서:

default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate. 
Errors or runtime exceptions thrown during iteration or by the predicate 
are relayed to the caller.

흥미로운 주의:

The default implementation traverses all elements of the collection using its iterator(). 
Each matching element is removed using Iterator.remove().

송신원: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-

첫 번째 루프를 제거하여 솔루션을 리팩터링할 수도 있습니다.

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>(set);

for(Integer element : set)
   if(element % 2 == 0)
       removeCandidates.add(element);

set.removeAll(removeCandidates);

목재처럼 - "Java 8 Collection에는 remove If라는 멋진 방법이 있어 작업을 더 쉽고 안전하게 할 수 있습니다.

문제를 해결하는 코드는 다음과 같습니다.

set.removeIf((Integer element) -> {
    return (element % 2 == 0);
});

이제 세트에 홀수 값만 포함됩니다.

다음은 보다 현대적인 스트림 접근법입니다.

myIntegerSet.stream().filter((it) -> it % 2 != 0).collect(Collectors.toSet())

다만, 이것은 새로운 세트가 되기 때문에, 매우 큰 세트라면 메모리 제약이 문제가 될 수 있습니다.

편집: 이 답변의 이전 버전은 Apache Collection Utils를 제안했지만, 이는 스팀 발생 전이었습니다.

다른 가능한 해결책:

for(Object it : set.toArray()) { /* Create a copy */
    Integer element = (Integer)it;
    if(element % 2 == 0)
        set.remove(element);
}

또는 다음 중 하나를 선택합니다.

Integer[] copy = new Integer[set.size()];
set.toArray(copy);

for(Integer element : copy) {
    if(element % 2 == 0)
        set.remove(element);
}

언급URL : https://stackoverflow.com/questions/1110404/remove-elements-from-a-hashset-while-iterating

반응형