Question

    

Cette question a déjà une réponse ici:

         

Alors, si je tente de supprimer des éléments d'un Java HashSet tout en réitérant, je reçois un ConcurrentModificationException . Quelle est la meilleure façon de supprimer un sous-ensemble des éléments d'une HashSet comme dans l'exemple suivant?

Set<Integer> set = new HashSet<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

// Throws ConcurrentModificationException
for(Integer element : set)
    if(element % 2 == 0)
        set.remove(element);

Voici une solution, mais je ne pense pas qu'il est très élégant:

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

for(Integer element : set)
    if(element % 2 == 0)
        removeCandidates.add(element);

set.removeAll(removeCandidates);

Merci!

Était-ce utile?

La solution

Vous pouvez manuellement itérer sur les éléments de l'ensemble:

Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    if (element % 2 == 0) {
        iterator.remove();
    }
}

Vous verrez souvent ce modèle en utilisant une boucle de for plutôt qu'une boucle de while:

for (Iterator<Integer> i = set.iterator(); i.hasNext();) {
    Integer element = i.next();
    if (element % 2 == 0) {
        i.remove();
    }
}

Comme les gens l'ont souligné, en utilisant une boucle de for est préférable, car elle conserve la variable iterator (i dans ce cas) limitée à une portée plus limitée.

Autres conseils

La raison pour laquelle vous obtenez un ConcurrentModificationException est parce qu'une entrée est supprimée par Set.remove () par opposition à Iterator.remove () . Si une entrée est supprimée par Set.remove () alors qu'une itération est fait, vous obtiendrez un ConcurrentModificationException. D'autre part, la suppression des entrées via Iterator.remove () alors que l'itération est pris en charge dans ce cas.

La nouvelle boucle est agréable, mais malheureusement, il ne fonctionne pas dans ce cas, parce que vous ne pouvez pas utiliser la référence Iterator.

Si vous devez supprimer une entrée en itération, vous devez utiliser la forme longue qui utilise directement l'itérateur.

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}

vous pouvez également factoriser votre solution retrait de la première boucle:

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>(set);

for(Integer element : set)
   if(element % 2 == 0)
       removeCandidates.add(element);

set.removeAll(removeCandidates);

Java 8 Collection a une méthode bien appelé removeif qui rend les choses plus facile et plus sûr. De l'API docs:

default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate. 
Errors or runtime exceptions thrown during iteration or by the predicate 
are relayed to the caller.

Note intéressante:

The default implementation traverses all elements of the collection using its iterator(). 
Each matching element is removed using Iterator.remove().

De: https: //docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-

Comme le bois dit - « Java 8 Collection a une méthode bien appelé removeif qui rend les choses plus facile et plus sûr »

Voici le code qui résoudre votre problème:

set.removeIf((Integer element) -> {
    return (element % 2 == 0);
});

Maintenant, votre jeu ne contient que des valeurs impaires.

-t-il besoin d'être tout en réitérant? Si tout ce que vous faites est le filtrage ou la sélection Je suggère d'utiliser Apache Commons CollectionUtils . Il y a là des outils puissants et il rend votre code « plus frais ».

Voici une implémentation qui devrait fournir ce dont vous avez besoin:

Set<Integer> myIntegerSet = new HashSet<Integer>();
// Integers loaded here
CollectionUtils.filter( myIntegerSet, new Predicate() {
                              public boolean evaluate(Object input) {
                                  return (((Integer) input) % 2 == 0);
                              }});

Si vous vous trouvez en utilisant le même genre de prédicat vous pouvez souvent tirer que sur dans une variable statique pour la réutilisation ... nommez quelque chose comme EVEN_NUMBER_PREDICATE. Certains peuvent voir que le code et le déclarer « difficile à lire », mais il semble plus propre quand vous tirez la prédicats en statique. Ensuite, il est facile de voir que nous faisons un CollectionUtils.filter(...) et qui semble plus lisible (pour moi) qu'un tas de boucles dans toute la création.

Une autre solution possible:

for(Object it : set.toArray()) { /* Create a copy */
    Integer element = (Integer)it;
    if(element % 2 == 0)
        set.remove(element);
}

Ou:

Integer[] copy = new Integer[set.size()];
set.toArray(copy);

for(Integer element : copy) {
    if(element % 2 == 0)
        set.remove(element);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top