Как избежать java.util.ConcurrentModificationException при переборе и удалении элементов из ArrayList - PullRequest
169 голосов
/ 12 ноября 2011

У меня есть ArrayList, который я хочу перебрать.Итерируя по нему, я должен удалить элементы одновременно.Очевидно, это выдает java.util.ConcurrentModificationException.

. Каков наилучший метод для решения этой проблемы?Должен ли я сначала клонировать список?

Я удаляю элементы не в самом цикле, а в другой части кода.

Мой код выглядит так:

public class Test() {
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff() {
        for (A a : abc) 
        a.doSomething();
    }

    public void removeA(A a) {
        abc.remove(a);
    }
}

a.doSomething может позвонить Test.removeA();

Ответы [ 18 ]

269 голосов
/ 12 ноября 2011

Два варианта:

  • Создать список значений, которые вы хотите удалить, добавив к этому списку в цикле, затем вызвать originalList.removeAll(valuesToRemove) в конце
  • Используйте метод remove() на самом итераторе.Обратите внимание, что это означает, что вы не можете использовать расширенный цикл for.

В качестве примера второго варианта удаление любых строк длиной более 5 из списка:

List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String value = iterator.next();
    if (value.length() > 5) {
        iterator.remove();
    }
}
16 голосов
/ 12 ноября 2011

Из JavaDocs ArrayList

Итераторы, возвращаемые итераторами этого класса и listIterator методы работают быстро: если список структурно изменен время после создания итератора, любым способом, кроме как через собственные методы удаления или добавления * итератора , итератор выдаст ConcurrentModificationException.

9 голосов
/ 24 августа 2016

Вы пытаетесь удалить значение из списка в расширенном цикле «for», что невозможно, даже если вы применили какой-то трюк (который вы сделали в своем коде).Лучший способ - это кодировать уровень итератора, как советуют другие.

Интересно, как люди не предложили традиционный подход для циклического подхода?

for( int i = 0; i < lStringList.size(); i++ )
{
    String lValue = lStringList.get( i );
    if(lValue.equals("_Not_Required"))
    {
         lStringList.remove(lValue);
         i--; 
    }  
}

Это также работает.

8 голосов
/ 23 января 2017

Вам действительно нужно просто выполнить итерацию массива традиционным способом

Каждый раз, когда вы удаляете элемент из списка, элементы после будут перемещаться вперед.Пока вы не измените элементы, кроме итеративного, следующий код должен работать.

public class Test(){
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff(){
        for(int i = (abc.size() - 1); i >= 0; i--) 
            abc.get(i).doSomething();
    }

    public void removeA(A a){
        abc.remove(a);
    }
}
7 голосов
/ 05 марта 2017

В Java 8 вы можете использовать интерфейс коллекции и сделать это, вызвав метод removeIf:

yourList.removeIf((A a) -> a.value == 2);

Более подробную информацию можно найти здесь

6 голосов
/ 21 июля 2017

Выполните цикл обычным способом, ошибка java.util.ConcurrentModificationException связана с элементами, к которым осуществляется доступ.

Так что попробуйте:

for(int i = 0; i < list.size(); i++){
    lista.get(i).action();
}
5 голосов
/ 12 ноября 2011

Один из вариантов - изменить метод removeA на этот -

public void removeA(A a,Iterator<A> iterator) {
     iterator.remove(a);
     }

Но это будет означать, что ваш doSomething() должен быть в состоянии передать iterator методу remove. Не очень хорошая идея.

Можете ли вы сделать это в два этапа: В первом цикле, когда вы перебираете список, вместо удаления выбранных элементов, пометьте их как для удаления . Для этого вы можете просто скопировать эти элементы (поверхностное копирование) в другой List.

Затем, как только ваша итерация будет завершена, просто сделайте removeAll из первого списка всех элементов во втором списке.

5 голосов
/ 06 июля 2016

Вот пример, в котором я использую другой список для добавления объектов для удаления, затем я использую stream.foreach для удаления элементов из исходного списка:

private ObservableList<CustomerTableEntry> customersTableViewItems = FXCollections.observableArrayList();
...
private void removeOutdatedRowsElementsFromCustomerView()
{
    ObjectProperty<TimeStamp> currentTimestamp = new SimpleObjectProperty<>(TimeStamp.getCurrentTime());
    long diff;
    long diffSeconds;
    List<Object> objectsToRemove = new ArrayList<>();
    for(CustomerTableEntry item: customersTableViewItems) {
        diff = currentTimestamp.getValue().getTime() - item.timestamp.getValue().getTime();
        diffSeconds = diff / 1000 % 60;
        if(diffSeconds > 10) {
            // Element has been idle for too long, meaning no communication, hence remove it
            System.out.printf("- Idle element [%s] - will be removed\n", item.getUserName());
            objectsToRemove.add(item);
        }
    }
    objectsToRemove.stream().forEach(o -> customersTableViewItems.remove(o));
}
4 голосов
/ 01 февраля 2018

Во время итерации списка, если вы хотите удалить элемент, это возможно. Давай посмотрим ниже мои примеры,

ArrayList<String>  names = new ArrayList<String>();
        names.add("abc");
        names.add("def");
        names.add("ghi");
        names.add("xyz");

У меня есть вышеприведенные имена списка Array. И я хочу удалить имя «def» из списка выше,

for(String name : names){
    if(name.equals("def")){
        names.remove("def");
    }
}

Приведенный выше код вызывает исключение ConcurrentModificationException , поскольку вы изменяете список во время итерации.

Итак, чтобы удалить имя "def" из Arraylist, выполнив следующие действия,

Iterator<String> itr = names.iterator();            
while(itr.hasNext()){
    String name = itr.next();
    if(name.equals("def")){
        itr.remove();
    }
}

Приведенный выше код, через итератор мы можем удалить имя "def" из Arraylist и попытаться напечатать массив, вы увидите вывод ниже.

Вывод: [abc, ghi, xyz]

3 голосов
/ 12 марта 2018

Вместо использования Для каждого цикла используйте обычный цикл for.например, приведенный ниже код удаляет все элементы в списке массивов без предоставления исключения java.util.ConcurrentModificationException.Вы можете изменить условие в цикле в соответствии с вашим вариантом использования.

   for(int i=0;i<abc.size();i++)  {

          e.remove(i);
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...