Мы все знаем, что вы не можете сделать следующее из-за ConcurrentModificationException
:
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
Но это, видимо, иногда работает, но не всегда. Вот некоторый конкретный код:
public static void main(String[] args) {
Collection<Integer> 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);
}
Это, конечно, приводит к:
Exception in thread "main" java.util.ConcurrentModificationException
Хотя несколько потоков этого не делают. Тем не мение.
Как лучше всего решить эту проблему? Как я могу удалить элемент из коллекции в цикле, не выбрасывая это исключение?
Я также использую произвольное Collection
здесь, не обязательно ArrayList
, так что вы не можете положиться get
.
java
collections
iteration
Клаудиу
источник
источник
Ответы:
Iterator.remove()
безопасно, вы можете использовать его так:Обратите внимание, что
Iterator.remove()
это единственный безопасный способ изменить коллекцию во время итерации; поведение не определено, если базовая коллекция модифицируется любым другим способом во время выполнения итерации.Источник: docs.oracle> Интерфейс коллекции
И точно так же, если у вас есть
ListIterator
и вы хотите добавить элементы, вы можете использовать ихListIterator#add
по той же причине, по которой вы можете их использоватьIterator#remove
- она предназначена для этого.В вашем случае вы пытались удалить из списка, но то же самое ограничение применяется при попытке
put
вMap
то время как итерация его содержания.источник
iterator.next()
вызов в цикл for? Если нет, может кто-нибудь объяснить, почему?List.add
в этом же смысле «необязателен», но вы бы не сказали, что «небезопасно» добавлять в список.Это работает:
Я предположил, что поскольку цикл foreach является синтаксическим сахаром для итерации, использование итератора не поможет ... но он предоставляет вам эту
.remove()
функциональность.источник
С Java 8 вы можете использовать новый
removeIf
метод . Применительно к вашему примеру:источник
removeIf
используетIterator
иwhile
цикл. Вы можете увидеть это наjava.util.Collection.java
ArrayList
переопределение по соображениям производительности. То, на что вы ссылаетесь, является только реализацией по умолчанию.equals
вообще не используется, поэтому его не нужно реализовывать. (Но, конечно, если вы используетеequals
в своем тесте, то он должен быть реализован так, как вы этого хотите.)Поскольку на этот вопрос уже дан ответ, т. Е. Лучший способ - использовать метод удаления объекта итератора, я бы подробно остановился на том месте, где выдается ошибка
"java.util.ConcurrentModificationException"
.Каждый класс коллекции есть отдельный класс , который реализует интерфейс итератора и предоставляет методы , как
next()
,remove()
иhasNext()
.Код для следующего выглядит примерно так ...
Здесь метод
checkForComodification
реализован какИтак, как вы можете видеть, если вы явно пытаетесь удалить элемент из коллекции. Это приводит к тому,
modCount
что оно отличается от другогоexpectedModCount
, что приводит к исключениюConcurrentModificationException
.источник
Вы можете использовать итератор непосредственно, как вы упомянули, или оставить вторую коллекцию и добавить каждый элемент, который вы хотите удалить, в новую коллекцию, а затем удалить все в конце. Это позволяет вам продолжать использовать безопасность типов для каждого цикла за счет увеличения использования памяти и времени процессора (не должно быть большой проблемой, если у вас нет действительно очень больших списков или действительно старого компьютера)
источник
В таких случаях обычная хитрость - это (было?) Идти назад:
Тем не менее, я более чем счастлив, что у вас есть лучшие способы в Java 8, например,
removeIf
илиfilter
в потоках.источник
ArrayList
s или подобных коллекций.for(int i = l.size(); i-->0;) {
?Тот же ответ, что и у Клавдия с циклом for:
источник
В Eclipse Collections будет работать метод,
removeIf
определенный в MutableCollection :С помощью синтаксиса Java 8 Lambda это можно записать следующим образом:
Здесь необходим вызов,
Predicates.cast()
потому чтоremoveIf
вjava.util.Collection
интерфейсе Java 8 был добавлен метод по умолчанию .Примечание: я являюсь коммиттером для Eclipse Collections .
источник
Сделайте копию существующего списка и переберите новую копию.
источник
Люди утверждают, что нельзя удалить из коллекции, повторяемой циклом foreach. Я просто хотел указать, что это технически неверно, и точно описать (я знаю, что вопрос ОП настолько сложен, чтобы избежать знания этого) код, лежащий в основе этого предположения:
Дело не в том, что вы не можете удалить из повторения,
Colletion
а в том, что вы не сможете продолжить итерацию, как только это сделаете. Следовательно,break
в коде выше.Извините, если этот ответ является несколько специализированным вариантом использования и больше подходит для исходного потока, из которого я прибыл сюда, что он помечен как дубликат (несмотря на то, что этот поток выглядит более нюансированным) этого и заблокирован.
источник
С традиционным для цикла
источник
i++
защиты цикла, а не внутри тела цикла.i++
приращение не было условным - теперь я вижу, поэтому вы делаете это в теле :)A
ListIterator
позволяет добавлять или удалять элементы в списке. Предположим, у вас есть списокCar
объектов:источник
previous
метод.У меня есть предложение по проблеме выше. Нет необходимости вторичного списка или дополнительного времени. Пожалуйста, найдите пример, который сделал бы то же самое, но по-другому.
Это позволит избежать исключения параллелизма.
источник
ArrayList
и поэтому на него нельзя положитьсяget()
. В противном случае, вероятно, хороший подход, хотя.Collection
-Collection
интерфейс не включаетget
. (ХотяList
интерфейс FWIW включает «get»).while
-looping aList
. Но +1 за этот ответ, потому что он пришел первым.ConcurrentHashMap или ConcurrentLinkedQueue или ConcurrentSkipListMap могут быть другой опцией, потому что они никогда не вызовут исключение ConcurrentModificationException, даже если вы удалите или добавите элемент.
источник
java.util.concurrent
упаковке. Некоторые другие похожие / общие сценарии использования из этого пакета являютсяCopyOnWriteArrayList
&CopyOnWriteArraySet
[но не ограничиваются ими].ConcurrentModificationException
, их использование в расширенном циклеIndexOutOfBoundsException
Я знаю, что этот вопрос слишком старый, чтобы быть о Java 8, но для тех, кто использует Java 8, вы можете легко использовать removeIf ():
источник
Другой способ - создать копию вашего arrayList:
источник
i
это не объект,index
а объект. Возможно, назвать этоobj
было бы более уместно.источник
В случае ArrayList: remove (int index) - if (index - позиция последнего элемента), он избегает без
System.arraycopy()
и не требует для этого времени.время массива увеличивается, если (индекс уменьшается), кстати, элементы списка также уменьшаются!
лучший эффективный способ удаления - удаление его элементов в порядке убывания:
while(list.size()>0)list.remove(list.size()-1);
// принимает O (1)while(list.size()>0)list.remove(0);
// принимает O (factorial (n))источник
Улов будет после удаления элемента из списка, если вы пропустите внутренний вызов iterator.next (). это все еще работает! Хотя я не предлагаю писать такой код, это помогает понять концепцию, стоящую за ним :-)
Ура!
источник
Пример модификации потока безопасной коллекции:
источник
Я знаю, что этот вопрос предполагает просто
Collection
, а не конкретнееList
. Но для тех, кто читает этот вопрос и которые действительно работают соList
ссылкой, вы можете вместо этогоConcurrentModificationException
использоватьwhile
-loop (изменяя его), если вы хотите избежатьIterator
(либо если вы хотите избежать этого в целом, либо избегать его специально для достижения порядок зацикливания, отличный от начала и до конца в каждом элементе [который, я считаю, является единственным порядкомIterator
который может сделать сам)]:* Обновление: см. Комментарии ниже, которые поясняют, что аналогичное также возможно с традиционным циклом -for.
Нет ConcurrentModificationException из этого кода.
Там мы видим, что цикл не начинается в начале и не останавливается на каждом элементе (что, я считаю,
Iterator
само по себе не может).Мы также видим,
get
что вызываетсяlist
, что нельзя было бы сделать, если бы его ссылка была простоCollection
(вместо более конкретногоList
типаCollection
) -List
интерфейс включаетget
, аCollection
интерфейс - нет. Если бы не это различие, тоlist
ссылка могла бы вместо этого бытьCollection
[и, следовательно, технически этот Ответ был бы тогда прямым ответом, а не тангенциальным ответом].FWIWW тот же код по-прежнему работает после изменения, чтобы начать с начала и до остановки на каждом элементе (как
Iterator
порядок):источник
ConcurrentModificationException
, но не традиционный -дль петля (которые другие ответ использование) - не понимая , что перед тем , как , почему я был мотивирован , чтобы написать этот ответ (я ошибочно подумал тогда, что это все циклы for, которые будут выбрасывать исключение).Одним из решений может быть поворот списка и удаление первого элемента, чтобы избежать исключения ConcurrentModificationException или IndexOutOfBoundsException
источник
Попробуйте это (удаляет все элементы в списке, которые равны
i
):источник
Вы также можете использовать рекурсию
Рекурсия в Java - это процесс, в котором метод вызывает себя непрерывно. Метод в Java, который вызывает себя, называется рекурсивным методом.
источник
это может быть не лучшим способом, но для большинства небольших случаев это должно быть приемлемо:
Я не помню, откуда я это прочитал ... для справедливости я сделаю эту вики в надежде, что кто-то найдет ее или просто не заработает репутацию, которой я не заслуживаю.
источник