Ответ 1
Используйте Iterator
и вызывайте remove()
:
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
Я пытаюсь удалить некоторые элементы из ArrayList
, итерации следующим образом:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Конечно, я получаю ConcurrentModificationException
при попытке удалить элементы из списка одновременно при повторении myArrayList
. Есть ли какое-то простое решение для решения этой проблемы?
Используйте Iterator
и вызывайте remove()
:
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
В качестве альтернативы всем остальным ответам я всегда делал что-то вроде этого:
List<String> toRemove = new ArrayList<>();
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));
Он удалит элементы в списке, для которых выполняется некоторое условие
Вы должны использовать метод iterator remove(), что означает, что для цикла не было усилено:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
Нет, нет, НЕТ!
В одиночных задачах вам не нужно использовать Iterator, более того, CopyOnWriteArrayList (из-за повышения производительности).
Решение намного проще: попытайтесь использовать канонический цикл для цикла, а не для каждого цикла.
В соответствии с владельцами авторских прав Java (несколько лет назад Sun, теперь Oracle) для каждого руководства по циклам, он использует итератор для просмотра коллекции и просто скрывает его, чтобы сделать код выглядит лучше. Но, к сожалению, как мы видим, это вызвало больше проблем, чем прибыли, иначе эта тема не возникла бы.
Например, этот код приведет к 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
}
}
Итак, попробуйте использовать метод индексирования для итерации по коллекциям и избегайте цикла for-each, поскольку они не эквивалентны! Для каждого цикла используются некоторые внутренние итераторы, которые проверяют модификацию коллекции и исключают исключение 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);
Если вы хотите изменить свой список во время обхода, вам нужно использовать 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();
}
}
Один альтернативный метод преобразует ваши List
в array
, перебирает их и удаляет их непосредственно из 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 remove() для удаления объекта из базового объекта коллекции. Но в этом случае вы можете удалить один и тот же объект, а не любой другой объект из списка.
из здесь