web-dev-qa-db-fra.com

ConcurrentModificationException pour ArrayList

J'ai le code suivant:

private String toString(List<DrugStrength> aDrugStrengthList) {
    StringBuilder str = new StringBuilder();
        for (DrugStrength aDrugStrength : aDrugStrengthList) {
            if (!aDrugStrength.isValidDrugDescription()) {
                aDrugStrengthList.remove(aDrugStrength);
            }
        }
        str.append(aDrugStrengthList);
        if (str.indexOf("]") != -1) {
            str.insert(str.lastIndexOf("]"), "\n          " );
        }
    return str.toString();
}

Lorsque j'essaie de l'exécuter, j'obtiens ConcurrentModificationException. Quelqu'un peut-il expliquer pourquoi cela se produit, même si le code s'exécute dans le même thread? Et comment pourrais-je l'éviter?

77
mabuzer

Vous ne pouvez pas supprimer de la liste si vous le parcourez avec "pour chaque" boucle. Vous pouvez utiliser Iterator. Remplacer:

for (DrugStrength aDrugStrength : aDrugStrengthList) {
    if (!aDrugStrength.isValidDrugDescription()) {
        aDrugStrengthList.remove(aDrugStrength);
    }
}

Avec:

for (Iterator<DrugStrength> it = aDrugStrengthList.iterator(); it.hasNext(); ) {
    DrugStrength aDrugStrength = it.next();
    if (!aDrugStrength.isValidDrugDescription()) {
        it.remove();
    }
}
157
Konrad Garus

Comme le disent les autres réponses, vous ne pouvez pas supprimer un élément d'une collection sur laquelle vous effectuez une itération. Vous pouvez contourner ce problème en utilisant explicitement un Iterator et en y supprimant l'élément.

Iterator<Item> iter = list.iterator();
while(iter.hasNext()) {
  Item blah = iter.next();
  if(...) {
    iter.remove(); // Removes the 'current' item
  }
}
24
Edward Dale

J'aime les ordres inverses pour les boucles tels que:

int size = list.size();
for (int i = size - 1; i >= 0; i--) {
    if(remove){
        list.remove(i);
    }
}

car il ne nécessite pas l'apprentissage de nouvelles structures de données ou de classes.

15
froman

il devrait y avoir une implémentation simultanée de l'interface de liste prenant en charge une telle opération.

essayez Java.util.concurrent.CopyOnWriteArrayList.class

7
idiotgenius

En effectuant une itération dans la boucle, vous essayez de modifier la valeur de liste dans l'opération remove (). Cela entraînera ConcurrentModificationException.

Suivez le code ci-dessous, qui réalisera ce que vous voulez et ne lancera aucune exception.

private String toString(List aDrugStrengthList) {
        StringBuilder str = new StringBuilder();
    List removalList = new ArrayList();
    for (DrugStrength aDrugStrength : aDrugStrengthList) {
        if (!aDrugStrength.isValidDrugDescription()) {
            removalList.add(aDrugStrength);
        }
    }
    aDrugStrengthList.removeAll(removalList);
    str.append(aDrugStrengthList);
    if (str.indexOf("]") != -1) {
        str.insert(str.lastIndexOf("]"), "\n          " );
    }
    return str.toString();
}
5
bragboy

Nous pouvons utiliser des classes de collection simultanées pour éviter la ConcurrentModificationException lors d'une itération sur une collection, par exemple CopyOnWriteArrayList au lieu de ArrayList.

Vérifiez ce post pour ConcurrentHashMap

http://www.journaldev.com/122/hashmap-vs-concurrenthashmap-%E2%80%93-example-and-exploring-iterator