web-dev-qa-db-fra.com

java.util.ConcurrentModificationException avec itérateur

Je sais que si j'essaierais de supprimer de la collection une boucle en boucle avec la boucle simple, j'obtiendrai cette exception: Java.util.ConcurrentModificationException. Mais j'utilise Iterator et il génère toujours cette exception. Une idée pourquoi et comment le résoudre?

HashSet<TableRecord> tableRecords = new HashSet<>();

...

    for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
        TableRecord record = iterator.next();
        if (record.getDependency() == null) {
            for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
                TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
                if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
                    iterator.remove();
                }
            }
        }
    }
19
user2219247

Vous devez utiliser iterator.remove() au lieu de tableRecords.remove()

Vous pouvez supprimer des éléments d'une liste sur laquelle vous effectuez une itération uniquement si vous utilisez la méthode remove de l'itérateur.

MODIFIER :

Lorsque vous créez un itérateur, il commence à compter les modifications qui ont été appliquées à la collection. Si l'itérateur détecte que certaines modifications ont été apportées sans utiliser sa méthode (ou en utilisant un autre itérateur sur la même collection), il ne peut plus garantir qu'il ne passera pas deux fois sur le même élément ou n'en ignorera pas un, il lève donc cette exception.

Cela signifie que vous devez changer votre code pour ne supprimer que les éléments via iterator.remove (et avec un seul itérateur).

OR

dressez une liste d'éléments à supprimer, puis supprimez-les une fois l'itération terminée.

32
Arnaud Denoyelle

La propriété Iterator fail-fast vérifie toute modification dans le fichier la structure de la collection sous-jacente à chaque fois que nous essayons d’obtenir le élément suivant. Si des modifications sont trouvées, il jette ConcurrentModificationException. Toutes les implémentations d'Iterator Dans Collection, les classes ont une conception rapide, sauf le concurrent classes de collection telles que ConcurrentHashMap et CopyOnWriteArrayList.

Source: Google

Vous comprendrez mieux avec un exemple écrit ci-dessous: -

import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.Iterator;
import Java.util.List;

public class IteratorExp {
    public static void main(String... q) {
        //CASE - ONE
        List<String> strList = new ArrayList<>(Arrays.asList("a", "b", "c"));
        Iterator<String> itr = strList.iterator();
        /*
         * strList.add("e"); strList.add("f"); strList.add("g");
         */
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        /*
         * Exception in thread "main" Java.util.ConcurrentModificationException
         * at Java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
         * Java.util.ArrayList$Itr.next(Unknown Source) at
         * IteratorExp.main(IteratorExp.Java:14)
         */

        //CASE - TWO 
        List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0));
        Iterator<Integer> itrOne = intList.iterator();
        Iterator<Integer> itrTwo = intList.iterator();
        for (; itrOne.hasNext();) {
            if (itrOne.next().equals(5)) {
                itrOne.remove(); // #1
                //intList.remove(itrOne.next()); // #2
            }
        }
        for (; itrTwo.hasNext();) {
            if (itrTwo.next().equals(5)) {
                itrTwo.remove(); // #1
                //intList.remove(itrTwo.next()); // #2
            }
        }

        /*
         * Exception in thread "main" Java.util.ConcurrentModificationException
         * at Java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
         * Java.util.ArrayList$Itr.next(Unknown Source) at
         * IteratorExp.main(IteratorExp.Java:35)
         */
    }
}
1

Le problème est que vous avez deux itérateurs en même temps et qu'ils se "combattent" l'un contre l'autre. Le moyen le plus simple de résoudre le problème consiste simplement à sortir de la boucle intérieure si vous trouvez une correspondance:

for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
    TableRecord record = iterator.next();
    if (record.getDependency() == null) {
        for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
            TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
            if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
                iterator.remove();
                break; // ADD THIS LINE
            }
        }
    }
}

Java Iterators est conçu pour "échouer rapidement" chaque fois que le conteneur sous-jacent est modifié sans avoir été modifié à l'aide de la variable Iterator. Vous utilisez des itérateurs imbriqués, de sorte que toute opération remove() émise pour l'un entraînera l'autre à émettre une Exception si elle continue à être utilisée. Pour cette raison, si vous devez émettre une remove(), vous devrez le faire sur l'itérateur "externe" (ce que vous faites) et cesser d'utiliser le deuxième itérateur par la suite (ce que fait la déclaration break ajoutée).

0
sigpwned

Le contrat pour l'itérateur de HashSet est que vous ne pouvez pas supprimer du hachage autrement que par la méthode remove de cet itérateur spécifique. Du point de vue de dependencyIt, vous avez supprimé un élément autrement qu'en appelant sa méthode remove afin qu'il lève un ConcurrentModificationException.

Il semble que vous souhaitiez supprimer des enregistrements de votre hashset lorsqu'ils ont le même identifiant d'enregistrement. Ne serait-il pas plus facile de remplacer les méthodes equals et hashcode de vos enregistrements pour garantir que les enregistrements ayant le même identifiant soient égaux et possèdent le même hashcode? (si cela a un sens bien sûr)

0
assylias