Как избежать java.утиль.ConcurrentModificationException при итерации и удалении элементов из ArrayList



у меня есть ArrayList, который я хочу перебрать. При итерации над ним я должен удалить элементы в то же время. Очевидно, что это бросает!--1-->.



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



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



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



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();

491   15  

15 ответов:

два варианта:

  • создать список значений, которые вы хотите удалить, добавлять к этому списку в цикле, затем вызовите 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();
    }
}

из JavaDocs ArrayList

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

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

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

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);
    }
}

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

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

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

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

один из вариантов-изменить removeA способ для этого -

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

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

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

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

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

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));
}

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

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

for(int i = 0; i < list.size(); i++){
    lista.get(i).action();
}

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

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

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

сделать что-то простое, как это:

for (Object object: (ArrayList<String>) list.clone()) {
    list.remove(object);
}

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

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

          e.remove(i);
        }

" должен ли я сначала клонировать список?"

Это будет самое простое решение, удалить из Клона, и скопируйте клон после удаления.

пример из моей игры rummikub:

SuppressWarnings("unchecked")
public void removeStones() {
  ArrayList<Stone> clone = (ArrayList<Stone>) stones.clone();
  // remove the stones moved to the table
  for (Stone stone : stones) {
      if (stone.isOnTable()) {
         clone.remove(stone);
      }
  }
  stones = (ArrayList<Stone>) clone.clone();
  sortStones();
}

альтернативное решение Java 8 с использованием stream:

        theList = theList.stream()
            .filter(element -> !shouldBeRemoved(element))
            .collect(Collectors.toList());

в Java 7 вместо этого можно использовать Guava:

        theList = FluentIterable.from(theList)
            .filter(new Predicate<String>() {
                @Override
                public boolean apply(String element) {
                    return !shouldBeRemoved(element);
                }
            })
            .toImmutableList();

обратите внимание, что пример Guava приводит к неизменяемому списку, который может быть или не быть тем, что вы хотите.

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

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

у меня есть выше имена списка массивов. И я хочу удалить имя " 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]

Если ваша цель, чтобы удалить все элементы из списка, вы можете перебрать каждый элемент, а затем вызвать:

list.clear()

Я прибываю поздно я знаю, но я отвечаю на это, потому что я думаю, что это решение является простым и элегантным:

List<String> listFixed = new ArrayList<String>();
List<String> dynamicList = new ArrayList<String>();

public void fillingList() {
    listFixed.add("Andrea");
    listFixed.add("Susana");
    listFixed.add("Oscar");
    listFixed.add("Valeria");
    listFixed.add("Kathy");
    listFixed.add("Laura");
    listFixed.add("Ana");
    listFixed.add("Becker");
    listFixed.add("Abraham");
    dynamicList.addAll(listFixed);
}

public void updatingListFixed() {
    for (String newList : dynamicList) {
        if (!listFixed.contains(newList)) {
            listFixed.add(newList);
        }
    }

    //this is for add elements if you want eraser also 

    String removeRegister="";
    for (String fixedList : listFixed) {
        if (!dynamicList.contains(fixedList)) {
            removeResgister = fixedList;
        }
    }
    fixedList.remove(removeRegister);
}

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

Comments

    Ничего не найдено.