Эта операция безопасна? - PullRequest
       9

Эта операция безопасна?

2 голосов
/ 01 февраля 2011

Безопасна ли эта операция?если нет, то чтобы сделать то же самое, как написать код ??

Set<Object> set;

......

for (Object o: set) {
    if (some_condition) {
        set.remove(o);
    }
}

Ответы [ 4 ]

8 голосов
/ 01 февраля 2011

Нет, это не так - потому что вам не разрешено изменять коллекцию, для которой вы выполняете итерацию, кроме как через итератор.Существуют различные варианты решения этой проблемы.Вот один из них:

for (Iterator<Object> iterator = set.iterator(); iterator.hasNext(); )
{
    Object value = iterator.next();
    if (someCondition)
    {
        iterator.remove();
    }
}

Другой вариант - создать отдельный список элементов для удаления, а затем удалить их все после , после которого вы перебрали набор:

List<Object> itemsToRemove = new ArrayList<Object>();
for (Object x in set)
{
    if (someCondition)
    {
        itemsToRemove.add(x);
    }
}

set.removeAll(itemsToRemove);
2 голосов
/ 01 февраля 2011

Нет - при первом выполнении вашего условия вы получите ConcurrentModificationException на следующей итерации.

В целом, небезопасно изменять коллекцию напрямую при ее итерации, и в этой ситуации итераторы «отказоустойчивы» (поскольку в целом, когда они могут обнаруживать изменения, нет общий способ разобраться, как с ними справиться).

В этой ситуации одна идиома, которая работает, - это использовать собственный метод Итератора remove(). Удаление элемента таким контролируемым образом держит итератор в курсе того, что происходит, и имеет согласованную семантику того, что должно происходить с порядком итерации, и поэтому работает так, как вы ожидаете:

Iterator<Object> iter = set.iterator();
while (iter.hasNext()) {
   final Object o = iter.next();
   if (some_condition) {
      iter.remove();
   }
}

Обратите внимание, что remove() является «необязательной операцией», и не все итераторы коллекций поддерживают ее. Если вы застряли в этой ситуации, альтернативой, которая всегда будет работать, является получение копии набора, итерации по , и удаление объектов из оригинального набора, например, так :

Set<Object> copy = new HashSet<Object>(set);
for (Object o: copy) {
    if (some_condition) {
        set.remove(o);
    }
}

Поскольку итерация завершена copy, в то время как изменения происходят с set, одновременных изменений не происходит, и итератор доволен.

1 голос
/ 01 февраля 2011

Теперь он выдаст ConcurrentModificationException.

Цикл for() использует внутренне Iterator, который сохраняет количество редактирования, и если это количество редактирования не совпадает с поддержкой Set 's счетчик редактирования, он выдает ConcurrentModificationException

Конечно, это зависит от фактической реализации коллекции, но это документированное поведение, например, в HashSet документах :

Итераторы, возвращаемые методом итератора этого класса, не подвержены сбоям: если набор изменяется в любое время после создания итератора, любым способом, кроме как через собственный метод удаления итератора, IteratorConcurrentModificationException.Таким образом, перед одновременной модификацией итератор быстро и чисто дает сбой, вместо того, чтобы рисковать произвольным недетерминированным поведением в неопределенное время в будущем.

0 голосов
/ 01 февраля 2011
    Set<Object> set;

    ......

    Set<Object> objectsToBeDeleted = new HashSet<Object>();
    for (Object o : set) {
        if (some_condition) {
            objectsToBeDeleted.add(o);
        }
    }

    for (Object o : objectsToBeDeleted) {
        set.remove(o);
    }
...