Comment la méthode remove de L'itérateur supprime réellement un objet
Nous savons tous que le moyen le plus sûr "et probablement le seul" de supprimer un objet d'une collection tout en l'itérant, est de récupérer d'abord le Iterator
, d'effectuer une boucle et de supprimer si nécessaire;
Iterator iter=Collection.iterator();
while(iter.hasNext()){
Object o=iter.next()
if(o.equals(what i'm looking for)){
iter.remove();
}
}
Ce que je voudrais comprendre, et malheureusement je n'ai pas trouvé d'explication technique approfondie, c'est comment cette suppression est effectuée,
Si:
for(Object o:myCollection().getObjects()){
if(o.equals(what i'm looking for)){
myCollection.remove(o);
}
}
Va lancer un ConcurrentModificationException
, Que fait" en termes techniques " Iterator.remove()
? Supprime-t-il l'objet, brise la boucle et redémarre-t-il la boucle?
, je vois dans la documentation officielle:
"supprime l'élément courant. Lance
IllegalStateException
si un on tente d'appeler {[6] } qui n'est pas précédé d'un appel à prochain( )."
La partie "supprime l'élément courant", me fait penser à la même situation qui se passe exactement dans une boucle" régulière " = > (effectuer un test d'égalité et supprimer si nécessaire), mais pourquoi la boucle D'itérateur ConcurrentModification-safe?
2 réponses
La façon dont iterator supprime exactement les éléments dépend de son implémentation, qui peut être différente pour différentes Collections. Certainement, il ne rompt pas la boucle dans laquelle vous êtes. Je viens de regarder comment ArrayList iterator est implémenté et voici le code:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
Donc, il vérifie les modifications simultanées, supprime l'élément en utilisant la méthode ArrayList publique remove , et incrémente le compteur des modifications de la liste afin que ConcurrentModificationException ne soit pas lancé lors de la prochaine itération.
La raison pour laquelle vous ne pouvez pas modifier une liste lors de l'itération est que l'itérateur doit savoir quoi retourner pour hasNext () et next ().
La façon dont cela est fait est spécifique à l'implémentation, mais vous pouvez jeter un oeil au code source de ArrayList/AbstractList/LinkedList etc.
Notez Également que dans certaines situations, vous pouvez utiliser un code comme celui-ci comme une alternative:
List<Foo> copyList = new ArrayList<>(origList);
for (Foo foo : copyList){
if (condition){
origList.remove(foo);
}
}
Mais ce code sera probablement un peu plus lent car la collection doit être copiée (copie uniquement) et l'élément à supprimer doit être recherché.
Notez également que si vous utilisez directement l'itérateur, il est recommandé d'utiliser une boucle for au lieu de while loop car cela limite la portée de la variable:
for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){
...
}