Вопрос по java, hashset, iteration, hashmap – Удалить элементы из HashSet во время итерации [duplicate]

107

This ques#ion already has an answer here:

I#era#ing #hrough a Collec#ion, avoiding Concurren#Modifica#ionExcep#ion when removing objec#s in a loop 23 answers

Итак, если я попытаюсь удалить элементы из JavaHashSe# итерируя, я получаюConcurren#Modifica#ionExcep#ion, Каков наилучший способ удалить подмножество элементов изHashSe# как в следующем примере?

Se#&l#;In#eger&g#; se# = new HashSe#&l#;In#eger&g#;();

for(in# i = 0; i &l#; 10; i++)
    se#.add(i);

// Throws Concurren#Modifica#ionExcep#ion
for(In#eger elemen# : se#)
    if(elemen# % 2 == 0)
        se#.remove(elemen#);

Вот решение, но я не думаю, что оно очень элегантное:

Se#&l#;In#eger&g#; se# = new HashSe#&l#;In#eger&g#;();
Collec#ion&l#;In#eger&g#; removeCandida#es = new LinkedLis#&l#;In#eger&g#;();

for(in# i = 0; i &l#; 10; i++)
    se#.add(i);

for(In#eger elemen# : se#)
    if(elemen# % 2 == 0)
        removeCandida#es.add(elemen#);

se#.removeAll(removeCandida#es);

Спасибо!

Ваш Ответ

7   ответов
2

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);
}
Это (или созданиеArrayList вне набора) - лучшее решение, если вам случится не только удалить существующие элементы, но и добавить новые в набор во время цикла.
4

что вы делаете, это фильтрация или выбор, я бы предложил использовать 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(...) и это кажется более читабельным (для меня), чем множество циклов по всему творению.

Этот ответ действительно начинает показывать его возраст ... Есть способ сделать это сейчас на Java-8, который, возможно, чище.
8

«В Java 8 Collection есть хороший метод removeIf, который делает вещи проще и безопаснее».

Вот код, который решает вашу проблему:

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

Теперь ваш набор содержит только нечетные значения.

10

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);
@RomainF. - Что вы подразумеваете под скрытой временной связью? Вы имеете в виду потокобезопасность? Во-вторых, я бы не советовал, но у решения есть свои плюсы. Супер легко читать и, следовательно, ремонтопригодны.
Я думаю, что этот ответ упускает из виду тот факт, что первый цикл был только там, чтобыHashSet из которого удаляются определенные элементы.
Я не рекомендовал бы это, поскольку это вводит скрытую временную связь.
отличный трюк, спасибо
Да, цикл for вызывает побочный эффект, но я согласен, что это может быть наиболее читаемым решением, если вы не используете Java 8. В противном случае просто используйте & quot; removeIf & quot; метод.
9

который делает вещи проще и безопаснее. Из документов 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-

Пример:integerSet.removeIf(integer-> integer.equals(5));
167

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 в данном случае) ограничивается меньшей областью применения.

Еслиwhile используется, тогда область действия итератора больше, чем нужно.
Я предпочитаю время, потому что это выглядит чище для меня. Область действия итератора не должна быть проблемой, если вы учитываете свой код. См. Книгу Becks & quot; Разработка через тестирование & quot; или Фаулера "Рефакторинг" больше о факторинге кода.
Я также используюfor себя. я использовалwhile надеюсь, сделать пример более понятным.
я предпочитаюfor вwhile, но каждому свое.
Я предпочитаюfor главным образом потому, что переменная итератора тогда ограничена областью действия цикла.
18

ConcurrentModificationException потому что запись удалена черезSet.remove() в отличие отIterator.remove(), Если запись удалена черезSet.remove() пока выполняется итерация, вы получите исключение ConcurrentModificationException. С другой стороны, удаление записей черезIterator.remove() в то время как итерация поддерживается в этом случае.

Новый цикл for хорош, но, к сожалению, он не работает в этом случае, потому что вы не можете использовать ссылку на итератор.

Если вам нужно удалить запись во время итерации, вам нужно использовать длинную форму, которая использует итератор напрямую.

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}
Тьфу. Исправлена. Благодарю.
На каком этапе находится «элемент»; инстанцирован?
Спасибо за это. Исправлена.
@ Разве твой код на самом деле не вызывает it.next ()?

Похожие вопросы