web-dev-qa-db-fra.com

Comment éviter une exception Java.util.ConcurrentModificationException lors de l'itération et de la suppression d'éléments d'un tableau de tableaux

J'ai un ArrayList que je veux parcourir. En itérant, je dois supprimer des éléments en même temps. Évidemment, cela jette un Java.util.ConcurrentModificationException.

Quelle est la meilleure pratique pour gérer ce problème? Dois-je d'abord cloner la liste?

Je supprime les éléments non pas dans la boucle mais dans une autre partie du code.

Mon code ressemble à ceci:

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 pourrait appeler Test.removeA();

150
anon

Deux options:

  • Créez une liste de valeurs que vous souhaitez supprimer, en ajoutant à cette liste dans la boucle, puis appelez originalList.removeAll(valuesToRemove) à la fin.
  • Utilisez la méthode remove() sur l'itérateur lui-même. Notez que cela signifie que vous ne pouvez pas utiliser la boucle for améliorée.

Comme exemple de la deuxième option, supprimer toutes les chaînes d'une longueur supérieure à 5 de la liste:

List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String value = iterator.next();
    if (value.length() > 5) {
        iterator.remove();
    }
}
255
Jon Skeet

À partir des JavaDocs de ArrayList

Les itérateurs retournés par les itérateurs et listIterator de cette classe les méthodes sont fail-fast: si la liste est structurellement modifiée à n’importe quel temps après la création de l'itérateur, de quelque manière que ce soit, sauf par le biais du fichier les méthodes propres à supprimer ou à ajouter de l'itérateur , l'itérateur jettera un ConcurrentModificationException.

14
Varun Achar

Vous devriez vraiment juste réitérer le tableau de la manière traditionnelle

Chaque fois que vous supprimez un élément de la liste, les éléments suivants seront activés. Tant que vous ne modifiez pas d'autres éléments que celui qui est itéré, le code suivant devrait fonctionner.

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

Vous essayez de supprimer la valeur de la liste dans la commande avancée "for loop", ce qui n’est pas possible, même si vous appliquez une astuce (que vous avez utilisée dans votre code) . Le meilleur moyen consiste à coder le niveau de l’itérateur comme indiqué ici.

Je me demande comment les gens n'ont pas suggéré l'approche traditionnelle de la boucle.

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

Cela fonctionne aussi bien.

7
suhas0sn07

Faites la boucle normalement, le Java.util.ConcurrentModificationException est une erreur liée aux éléments auxquels vous accédez.

Alors essayez:

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

Voici un exemple où j'utilise une liste différente pour ajouter les objets à supprimer, puis j'utilise stream.foreach pour supprimer des éléments de la liste d'origine:

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

Dans Java 8, vous pouvez utiliser l'interface de collection et appelez la méthode removeIf:

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

Plus d'informations peuvent être trouvées ici

5
ggeo

Une option consiste à modifier la méthode removeA en ceci -

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

Mais cela signifierait que votre doSomething() devrait pouvoir passer la iterator à la méthode remove. Ce n'est pas une bonne idée.

Pouvez-vous le faire en deux étapes: Dans la première boucle lorsque vous parcourez la liste, au lieu de supprimer les éléments sélectionnés, marque comme à supprimer. Pour cela, vous pouvez simplement copier ces éléments (copie superficielle) dans une autre List.

Ensuite, une fois que votre itération est terminée, faites simplement une removeAll dans la première liste de tous les éléments de la deuxième liste.

5
Bhaskar

Faites quelque chose de simple comme ça:

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

Au lieu d'utiliser Pour chaque boucle, utilisez normal pour boucle. par exemple, le code ci-dessous supprime tous les éléments de la liste de tableaux sans donner Java.util.ConcurrentModificationException. Vous pouvez modifier la condition dans la boucle en fonction de votre cas d'utilisation.

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

          e.remove(i);
        }
2
Shubham Chopra

Vous pouvez également utiliser CopyOnWriteArrayList au lieu d'un ArrayList. C'est la dernière approche recommandée à partir de JDK 1.5.

0
Pathikreet

En itérant la liste, il est possible de supprimer l'élément. Voyons ci-dessous mes exemples, 

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

J'ai les noms ci-dessus de la liste de tableau. Et je veux supprimer le nom "def" de la liste ci-dessus, 

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

Le code ci-dessus lève l'exception ConcurrentModificationException parce que vous modifiez la liste lors de l'itération. 

Donc, pour supprimer le nom "def" de Arraylist en procédant de cette façon,

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

Le code ci-dessus, par itérateur, nous pouvons supprimer le nom "def" de la liste et essayer d'imprimer le tableau, vous verrez la sortie ci-dessous.

Sortie: [abc, ghi, xyz]

0
Indra K

Si votre objectif est de supprimer tous les éléments de la liste, vous pouvez effectuer une itération sur chaque élément, puis appeler:

list.clear()
0
Gibolt

Utiliser Iterator au lieu de Array List

Faire convertir un ensemble en itérateur avec correspondance de type

Et passer à l'élément suivant et supprimer

Iterator<Insured> itr = insuredSet.iterator();
while (itr.hasNext()) { 
    itr.next();
    itr.remove();
}

Passer à la suivante est important ici car il devrait prendre l’index pour supprimer un élément.

0
user8009263

J'arrive tard je sais mais je réponds parce que je pense que cette solution est simple et élégante:

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

Tout cela sert à mettre à jour une liste à l’autre et vous pouvez tout créer à partir d’une seule liste Dans la méthode de mise à jour, vous vérifiez les deux listes et vous pouvez effacer ou ajouter des éléments entre des listes .

0

Une solution Java 8 alternative utilisant stream:

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

En Java 7, vous pouvez utiliser Guava à la place:

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

Notez que l'exemple de goyave donne une liste immuable qui peut être ou ne pas être ce que vous voulez.

0
Zsolt Sky

"Dois-je d'abord cloner la liste?"

Ce sera la solution la plus simple, retirez-le du clone et copiez-le à nouveau après son retrait.

Un exemple de mon jeu de 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();
}
0
Arjen Rodenhuis