Как избежать «ConcurrentModificationException» при удалении элементов из «ArrayList» при его повторении?

Я пытаюсь удалить некоторые элементы из ArrayList , повторяя его следующим образом:

 for (String str : myArrayList) { if (someCondition) { myArrayList.remove(str); } } 

Конечно, я получаю исключение ConcurrentModificationException при попытке удалить элементы из списка одновременно при итерации myArrayList . Есть ли какое-то простое решение для решения этой проблемы?

Используйте Iterator и вызовите remove() :

 Iterator iter = myArrayList.iterator(); while (iter.hasNext()) { String str = iter.next(); if (someCondition) iter.remove(); } 

В качестве альтернативы всем остальным я всегда делал что-то вроде этого:

 List toRemove = new ArrayList(); for (String str : myArrayList) { if (someCondition) { toRemove.add(str); } } myArrayList.removeAll(toRemove); 

Это позволит вам напрямую обращаться к iteratorу, но требует другого списка. Я всегда предпочитал этот маршрут по любой причине.

Пользователь Java 8 может сделать это: list.removeIf(...)

  List list = new ArrayList<>(Arrays.asList("a", "b", "c")); list.removeIf(e -> (someCondition)); 

Он удалит элементы в списке, для которых выполнено некоторое условие.

Вы должны использовать метод remove () iteratorа, что означает отсутствие расширенного для цикла:

 for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) { iterator.next(); if (someCondition) { iterator.remove(); } } 

Нет нет нет!

В одиночных задачах вам не нужно использовать Iterator, более того, CopyOnWriteArrayList (из-за повышения производительности).

Решение намного проще: попробуйте использовать канонический цикл for вместо цикла for-each .

Согласно владельцам авторских прав Java (несколько лет назад Sun, теперь Oracle) для каждого цикла , он использует iterator для просмотра коллекции и просто скрывает его, чтобы код выглядел лучше. Но, к сожалению, как мы видим, это создавало больше проблем, чем прибыль, иначе эта тема не возникла бы.

Например, этот код приведет к java.util.ConcurrentModificationException при вводе следующей итерации в модифицированном ArrayList:

  // process collection for (SomeClass currElement: testList) { SomeClass founDuplicate = findDuplicates(currElement); if (founDuplicate != null) { uniqueTestList.add(founDuplicate); testList.remove(testList.indexOf(currElement)); } } 

Но следующий код работает просто отлично:

  // process collection for (int i = 0; i < testList.size(); i++) { SomeClass currElement = testList.get(i); SomeClass founDuplicate = findDuplicates(currElement); if (founDuplicate != null) { uniqueTestList.add(founDuplicate); testList.remove(testList.indexOf(currElement)); i--; //to avoid skipping of shifted element } } 

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

 Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at TestFail.main(TestFail.java:43) 

Для многопоточности используются соответствующие многозадачные подходы (например, синхронизированное ключевое слово).

В то время как другие предлагаемые решения работают, если вы действительно хотите, чтобы решение было выполнено в streamовом режиме, вы должны заменить ArrayList на CopyOnWriteArrayList

  //List s = new ArrayList<>(); //Will throw exception List s = new CopyOnWriteArrayList<>(); s.add("B"); Iterator it = s.iterator(); s.add("A"); //Below removes only "B" from List while (it.hasNext()) { s.remove(it.next()); } System.out.println(s); 

Одним из альтернативных методов является преобразование вашего List в array , его повторение и удаление из List на основе вашей логики.

 List myList = new ArrayList(); // You can use either list or set myList.add("abc"); myList.add("abcd"); myList.add("abcde"); myList.add("abcdef"); myList.add("abcdefg"); Object[] obj = myList.toArray(); for(Object o:obj) { if(condition) myList.remove(o.toString()); } 

Если вы хотите изменить свой список во время обхода, вам нужно использовать Iterator . И тогда вы можете использовать iterator.remove() для удаления элементов во время обхода.

 List myArrayList = Collections.synchronizedList(new ArrayList()); //add your elements myArrayList.add(); myArrayList.add(); myArrayList.add(); synchronized(myArrayList) { Iterator i = myArrayList.iterator(); while (i.hasNext()){ Object object = i.next(); } } 

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

отсюда

  • Различия между IQueryable, List, IEnumerator?
  • Преобразовать Iterator в ArrayList
  • Как выбрать предмет по его вероятности?
  • Преобразовать список в список
  • Как вы перечислите список супертипов в список подтипов?
  • XmlSerializer сериализует общий список интерфейса
  • Получение исключения ConcurrentModificationException при удалении элемента из java.util.List во время итерации списка?
  • Сохраняется ли порядок элементов в списке JSON?
  • Сравните два списка различий
  • Как заставить Java-метод возвращать общий список любого типа?
  • как слить 2 List с удалением повторяющихся значений в C #
  • Давайте будем гением компьютера.