Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Мы все знаем, что из-за ConcurrentModificationException вы не можете делать 9X_java следующее:

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

Но иногда это срабатывает, но 9X_oraclejdk не всегда. Вот конкретный код:

public static void main(String[] args) {
    Collection 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);
}

Это, конечно 9X_iteration же, приводит к:

Exception in thread "main" java.util.ConcurrentModificationException

Даже если несколько потоков 9X_jdk этого не делают. В любом случае.

Как лучше 9X_collection всего решить эту проблему? Как я могу удалить 9X_oraclejdk элемент из коллекции в цикле, не вызывая 9X_collections этого исключения?

Я также использую здесь 9X_j2se произвольный Collection, не обязательно ArrayList, поэтому 9X_java-se вы не можете полагаться на get.

1283
1

  • Примечание для читателей: прочтите http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html, там может быть более простой способ до ...
17
Общее количество ответов: 17

Ответ #1

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Iterator.remove() безопасен, вы можете использовать его так:

List 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 iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

Обратите 9X_java-collections-api внимание, что Iterator.remove() - единственный безопасный 9X_java-api способ изменить коллекцию во время итерации; поведение 9X_core-java не определено, если базовая коллекция изменяется 9X_java любым другим способом во время выполнения итерации.

Источник: docs.oracle > The Collection Interface


И 9X_java-collections-api аналогично, если у вас есть ListIterator и вы хотите 9X_iteration добавить элементы, вы можете использовать ListIterator#add по той 9X_.java же причине, по которой вы можете использовать 9X_.java Iterator#remove - он предназначен для этого.


В вашем случае 9X_java-api вы пытались удалить из списка, но то же 9X_.java ограничение применяется при попытке вставить 9X_iteration put в Map при повторении его содержимого.

1661
5

  • Имейте в виду, что это медленнее по сравнению с ...

Ответ #2

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Это работает:

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

Я предположил, что, поскольку 9X_jre цикл foreach является синтаксическим сахаром 9X_iterative для итерации, использование итератора не 9X_java поможет ... но он дает вам эту функцию .remove().

357
2

  • +1, например, код для использования iter.remove() в контексте, которого в ответе Билла ...

Ответ #3

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

В Java 8 вы можете использовать the new removeIf method. Применяется 9X_java-se к вашему примеру:

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

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

234
2

  • @omerhakanbilici Некоторые реализации, такие как `ArrayList`, п ...

Ответ #4

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Поскольку на вопрос уже дан ответ, т.е. лучший 9X_j2se способ - использовать метод удаления объекта 9X_.java итератора, я бы подробно остановился на 9X_collections том месте, где возникает ошибка "java.util.ConcurrentModificationException".

Каждый 9X_java класс коллекции имеет частный класс, который 9X_java-api реализует интерфейс Iterator и предоставляет 9X_java-libraries такие методы, как next(), remove() и hasNext().

Код для next выглядит 9X_java-se примерно так ...

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

Здесь метод checkForComodification реализован 9X_java-api как

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

Итак, как видите, если вы явно попытаетесь 9X_java-libraries удалить элемент из коллекции. В результате 9X_iterative modCount отличается от expectedModCount, что приводит к исключению 9X_core-java ConcurrentModificationException.

44
1

  • Очень интересно. Спасибо! Я часто сам не вызываю remove(), вместо этого я предпочитаю очищать коллекцию после ее итерации. Не сказать, что это хороший образец, прос ...

Ответ #5

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Вы можете использовать итератор напрямую, как 9X_java-api вы упомянули, или сохранить вторую коллекцию 9X_java и добавить каждый элемент, который вы хотите 9X_collections удалить, в новую коллекцию, а затем удалитьAll 9X_.java в конце. Это позволяет вам продолжать использовать 9X_java-collections-api типобезопасность цикла for-each за счет 9X_openjdk увеличения использования памяти и времени 9X_jre процессора (не должно быть большой проблемой, если 9X_java-api у вас нет действительно очень больших списков 9X_java-collections-api или действительно старого компьютера)

public static void main(String[] args)
{
    Collection l = new ArrayList();
    Collection 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);
}

28
3

  • Достаточно справедливо, если вы больше ничего не делаете с итератором - его раскрытие упрощает выполнение таких вещей, как call .next() д ...

Ответ #6

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

В таких случаях распространенным трюком 9X_iterative является (был?) возврат назад:

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

Тем не менее, я 9X_jre более чем счастлив, что у вас есть лучшие 9X_collections способы использования Java 8, например removeIf или 9X_java-api filter в потоках.

19
2

  • индексы отличные. Если это так часто, почему бы вам не использовать `fo ...

Ответ #7

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Тот же ответ, что и у Claudius с циклом for:

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

9X_javax

17
0

Ответ #8

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

С Eclipse Collections будет работать метод removeIf, определенный в 9X_java MutableCollection:

MutableList 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 это можно записать 9X_java-api следующим образом:

MutableList 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() здесь необходим, поскольку 9X_openjdk метод removeIf по умолчанию был добавлен в интерфейс 9X_javax java.util.Collection в Java 8.

Примечание. Я являюсь коммиттером Eclipse Collections.

12
0

Ответ #9

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Сделайте копию существующего списка и перебирайте 9X_java-libraries новую копию.

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

11
2

  • @Antzi Это зависит от размера списка и плотности объектов внутри. Тем не м ...

Ответ #10

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

С традиционным циклом for

ArrayList myArray = new ArrayList<>();

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

9X_core-java

10
1

  • Ах, так что это действительно просто _enhanced_-for-loop, ...

Ответ #11

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Люди утверждают, что невозможно удалить из коллекции, повторяемой 9X_iteration циклом foreach. Я просто хотел указать, что 9X_iterative это технически неверно, и точно описать (я знаю, что 9X_iterative вопрос OP настолько сложен, чтобы не знать 9X_collections об этом) код, лежащий в основе этого предположения:

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
    }
}

Дело 9X_java-api не в том, что вы не можете удалить из повторяемого 9X_java-collections-api Colletion, а в том, что вы не можете продолжить итерацию, как 9X_iterative только вы это сделаете. Следовательно, break в 9X_javax приведенном выше коде.

Приносим извинения, если 9X_iteration этот ответ является несколько специализированным 9X_core-java вариантом использования и больше подходит 9X_jdk для исходного thread, откуда я пришел, этот ответ 9X_java-collections-api отмечен как дубликат (несмотря на то, что 9X_collections эта ветка выглядит более тонкой) этого и 9X_javax заблокирована.

10
0

Ответ #12

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

ConcurrentHashMap или ConcurrentLinkedQueue или ConcurrentSkipListMap могут быть другим вариантом, потому 9X_collection что они никогда не вызовут никаких исключений 9X_core-java ConcurrentModificationException, даже если 9X_oraclejdk вы удалите или добавите элемент.

4
0

Ответ #13

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Другой способ - использовать копию вашего 9X_java-api arrayList только для итерации:

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

4
2

  • Примечание: i - это не ин ...

Ответ #14

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

ListIterator позволяет добавлять или удалять элементы 9X_java-se в списке. Предположим, у вас есть список 9X_jre объектов Car:

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

for (ListIterator carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if ()
   { 
      carIterator().remove()
   }
   else if ()
   { 
      carIterator().add(aNewCar);
   }
}

2
0

Ответ #15

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Я знаю, что этот вопрос слишком стар, чтобы 9X_java-api касаться Java 8, но для тех, кто использует 9X_j2se Java 8, вы можете легко использовать removeIf():

Collection l = new ArrayList();

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

1
0

Ответ #16

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Теперь вы можете удалить с помощью следующего 9X_iteration кода

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

1
0

Ответ #17

Ответ на вопрос: Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Исключение одновременного изменения

  1. Однонитка
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        //throws ConcurrentModificationException
        list.remove(it.next());
    }
}

Решение: метод итератора remove()

Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        it.remove()
    }
}
  1. Многопоточность
  • копировать / преобразовывать и перебирать еще одну коллекцию. Для небольших коллекций
  • synchronize [About]
  • потокобезопасная коллекция [About]

9X_javax

1
0