Как избежать "ConcurrentModificationException" при удалении элементов из коллекции во время итерации это? [дубликат]
этот вопрос уже есть ответ здесь:
Я пытаюсь удалить некоторые элементы из ArrayList при повторении его так:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
конечно, я получаю ConcurrentModificationException при попытке удаление элементов из списка одновременно при итерации myArrayList. Есть ли какое-то простое решение для решения этой проблемы?
10 ответов:
в качестве альтернативы всем остальным ответам я всегда делал что-то вроде этого:
List<String> toRemove = new ArrayList<String>(); for (String str : myArrayList) { if (someCondition) { toRemove.add(str); } } myArrayList.removeAll(toRemove);Это позволит вам избежать необходимости иметь дело с итератором напрямую, но требует другого списка. Я всегда предпочитал этот маршрут по какой-то причине.
Java 8 пользователь может сделать это:
list.removeIf(...)List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c")); list.removeIf(e -> (someCondition));он удалит элементы в списке, для которых выполняется someCondition
вы должны использовать метод remove() итератора, что означает отсутствие улучшенного цикла for:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) { iterator.next(); if (someCondition) { iterator.remove(); } }
нет, нет, нет!
в одиночных threated задачах вам не нужно использовать итератор, кроме того, CopyOnWriteArrayList (из-за хита производительности).
решение гораздо проще: попробуйте использовать канонический цикл for вместо for-each loop.
согласно владельцам авторских прав Java (несколько лет назад Sun, теперь Oracle)для каждого направляющего выступа петли, он использует итератор, чтобы пройти через коллекцию и просто скрывает его, чтобы сделать код выглядит лучше. Но, к сожалению, как мы видим, это принесло больше проблем, чем прибыли, иначе эта тема не возникла бы.
например, этот код приведет к java.утиль.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 } }Итак, попробуйте использовать подход индексирования для итерации по коллекциям и избегать для каждого цикла, поскольку они не эквивалентны! Для-каждый цикл использует некоторые внутренние итераторы, которые проверяют модификацию коллекции и создают исключение 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)для многопоточности использовать соответствующие подходы в многозадачном режиме (например, синхронизированные сайта).
в то время как другие предлагаемые решения работают, если вы действительно хотите, чтобы решение было потокобезопасным, вы должны заменить ArrayList на CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception List<String> s = new CopyOnWriteArrayList<>(); s.add("B"); Iterator<String> it = s.iterator(); s.add("A"); //Below removes only "B" from List while (it.hasNext()) { s.remove(it.next()); } System.out.println(s);
один альтернативный метод-преобразовать ваш
Listtoarray, повторите их и удалите их непосредственно изListисходя из вашей логики.List<String> myList = new ArrayList<String>(); // 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() для удаления объекта из базового объекта коллекции. Но в этом случае вы можете удалить один и тот же объект, а не любой другой объект из списка.
с здесь
Comments