1269 votes

Efficace équivalent pour la suppression des éléments lors de l'itération de la Collection

Nous savons tous que vous ne pouvez pas faire ceci:

for (Object i : l) {
    if (condition(i))
        l.remove(i);
}

ConcurrentModificationException etc... ce qui, apparemment, fonctionne parfois, mais pas toujours. Voici un code spécifique:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<Integer>();

    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }

    for (Integer i : l) {
        if (i.intValue() == 5)
            l.remove(i);
    }

    System.out.println(l);
}

Ceci, bien sûr, les résultats:

Exception in thread "main" java.util.ConcurrentModificationException

... même si plusieurs threads ne sont pas... de toute façon.

Quelle est la meilleure solution à ce problème? "Meilleur" signifie ici la plupart du temps et de l'espace efficace (je me rends compte que vous ne pouvez pas toujours avoir les deux!)

Je suis également à l'aide d'un arbitraire Collection d'ici, pas nécessairement ArrayList, de sorte que vous ne pouvez pas compter sur get.

1654voto

Bill K Points 32115

Iterator.remove() est sûr.

Notez que Iterator.remove est la seule façon de modifier une collection au cours d'une itération; le comportement est indéfini si la collection sous-jacente est modifié de quelque autre manière, tandis que l'itération est en cours.

est de:

http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html

354voto

Claudiu Points 58398

Je suis bête:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next().intValue() == 5) {
        iter.remove();
    }
}

Je suppose que depuis une boucle foreach est sucre syntaxique pour réitérer, à l'aide d'un itérateur n'aide pas... mais il vous donne cette .remove() fonctionnalités.

233voto

assylias Points 102015

Avec Java 8, vous pouvez utiliser le nouveau removeIf méthode. Appliqué à votre exemple:

Collection<Integer> coll = new ArrayList<Integer>();
//populate

coll.removeIf(i -> i.intValue() == 5);

44voto

Ashish Points 709

Depuis que la question a déjà été répondu, c'est à dire le meilleur moyen est d'utiliser la méthode remove de l'itérateur de l'objet, je voudrais aller dans les détails de l'endroit où l'erreur "java.util.ConcurrentModificationException" est lancée.

Chaque classe de la collection privée de la classe qui implémente l'interface Iterator et fournit des méthodes comme la méthode next(), remove() et hasNext().

Le code pour la prochaine ressemble à quelque chose comme ça...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

Ici la méthode de la checkForComodification est mis en œuvre comme

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

Donc, comme vous pouvez le voir, si vous l'avez explicitement essayez de supprimer un élément de la collection. Il en résulte modCount différents de expectedModCount, résultant dans l'exception "ConcurrentModificationException".

28voto

RodeoClown Points 3949

Vous pouvez soit utiliser l'itérateur directement comme vous l'avez dit, ou bien de conserver une deuxième collecte et d'ajouter chaque élément que vous souhaitez supprimer de la nouvelle collection, puis removeAll à la fin. Cela vous permet de continuer à utiliser le type de sécurité de la boucle for-each, au prix de l'augmentation de l'utilisation de la mémoire et de temps processeur (ne devrait pas être un gros problème, sauf si vous avez vraiment, vraiment grand de listes ou d'un très ancien ordinateur)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<Integer>();
    for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5)
            itemsToRemove.add(i);
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X