Как избежать «ConcurrentModificationException» при удалении элементов из «ArrayList» при его повторении?
Я пытаюсь удалить некоторые элементы из ArrayList
, повторяя его следующим образом:
for (String str : myArrayList) { if (someCondition) { myArrayList.remove(str); } }
Конечно, я получаю исключение ConcurrentModificationException
при попытке удалить элементы из списка одновременно при итерации myArrayList
. Есть ли какое-то простое решение для решения этой проблемы?
- Linq выбирает объекты в списке, где существует IN (A, B, C)
- Как я могу распараллелить цикл for через C ++ std :: list с помощью OpenMP?
- C #: Как добавить подтипы в ListView
- Список безопасность streamов
- Почему у вас нет «List <List >» в Java?
- Эмулировать split () с dplyr group_by: вернуть список кадров данных
- Является ли list :: size () действительно O (n)?
- Получение элемента списка по индексу
- c # Когда мне следует использовать List и когда я должен использовать arraylist?
- Как удалить элементы из общего списка во время итерации по нему?
- Возможны ли 2-мерные списки в c #?
- Подключить Список к ListBox и увидеть изменения источника данных в ListBox
- Разница между Iterator и Listiterator?
Используйте 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 () для удаления объекта из базового объекта коллекции. Но в этом случае вы можете удалить один и тот же объект, а не любой другой объект из списка.
отсюда