문제

자바에서 요소를 제거하려고하면 해시 세트 반복하는 동안 나는 a 동시 변형 정보. 요소의 하위 집합을 제거하는 가장 좋은 방법은 무엇입니까? 해시 세트 다음 예에서와 같이?

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

당신은 종종 a를 사용 하여이 패턴을 볼 수 있습니다 for a가 아닌 루프 while 고리:

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

사람들이 지적했듯이 a for 루프는 반복자 변수를 유지하기 때문에 선호됩니다 (i 이 경우) 더 작은 범위에 국한됩니다.

다른 팁

당신이 얻는 이유 ConcurrentModificationException 항목이 제거되기 때문입니다 set.remove () 반대로 iterator.remove (). 항목이 제거 된 경우 set.remove () 반복이 완료되는 동안 동시 변형 외환을 얻게됩니다. 반면에, 항목 제거 iterator.remove () 이 경우 반복이 지원되는 동안.

루프의 새로운 것은 좋지만 불행히도 반복자 참조를 사용할 수 없기 때문에이 경우 작동하지 않습니다.

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

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

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

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에는 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.fredicate-

Timber와 같이 - "Java 8 Collection에는 remobif라는 멋진 방법이 있습니다.

다음은 문제를 해결하는 코드입니다.

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

이제 세트에는 홀수 값 만 포함되어 있습니다.

반복하는 동안 필요합니까? 당신이하고있는 모든 것이 필터링하거나 선택하는 것만으로도 Apache Commons 사용 제안을 할 것입니다. CollectionUtils. 거기에는 몇 가지 강력한 도구가 있으며 코드를 "쿨러"로 만듭니다.

다음은 필요한 것을 제공 해야하는 구현입니다.

Set<Integer> myIntegerSet = new HashSet<Integer>();
// Integers loaded here
CollectionUtils.filter( myIntegerSet, new Predicate() {
                              public boolean evaluate(Object input) {
                                  return (((Integer) input) % 2 == 0);
                              }});

동일한 종류의 술어를 자주 사용한다면 재사용을 위해 정적 변수로 꺼낼 수 있습니다. EVEN_NUMBER_PREDICATE. 어떤 사람들은 코드를보고 "읽기 어렵다"고 선언 할 수도 있지만 술어를 정적으로 꺼낼 때 더 깨끗해 보입니다. 그러면 우리가 CollectionUtils.filter(...) 그리고 그것은 창조 전체에 대한 많은 고리보다 더 읽기 쉬운 것 같습니다.

다른 가능한 해결책 :

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);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top