为什么java不要在foreach循环里进行元素的remove/add操作
PHPz
PHPz 2017-04-18 10:54:42
0
8
1199


选自《阿里巴巴JAVA开发手册》

图1代码执行情况是:解释删除1这个元素不会报错,但是删除2这个元素报错了,这个情况如何解释?

PHPz
PHPz

学习是最好的投资!

répondre à tous(8)
大家讲道理

La source de l'erreur checkForComodification() peut être connue grâce à l'erreur signalée. Si vous souhaitez éviter les erreurs, vous devez conserver modCount != expectedModCount comme false .
list.remove(Object) appellera la méthode fastRemove(int), et elle modifiera inévitablement modCount à ce moment-là, et une erreur se produira à ce moment-là.
Iterator<String> iterator = list.iterator() ; L'implémentation de cette méthode consiste à renvoyer une classe interne Itr (cette classe est utilisée dans le processus d'itération), mais la raison pour laquelle cela iterator.remove() ne provoque pas d'erreurs est due à la relation entre cette méthode et L'implémentation consiste à vérifier ArrayList.this.remove avant de faire le checkForComodfication réel et à faire remove après expectedModCount = modCount afin qu'aucune erreur ne se produise.

Itr.removeMise en œuvre

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();
            }
        }

S'il y a quelque chose qui ne va pas, veuillez le signaler @ChaCha哥 @puliuyinyi

Ty80

Dans le cas d'un seul thread, lors de la suppression d'éléments lors du parcours de la liste, vous devez utiliser la méthode Remove d'Iterator au lieu de la méthode Remove de List, sinon une ConcurrentModificationException se produira. Imaginez simplement si un enseignant compte le nombre d'élèves dans toute la classe, et si les élèves ne respectent pas les règles et ne sortent pas et n'entrent pas, l'enseignant ne pourra certainement pas les compter.

Dans le cas du multi-threading, référez-vous à un de mes blogs : http://xxgblog.com/2016/04/02...

Peter_Zhu

Tout d'abord, cela implique des opérations multi-thread. Iterator ne prend pas en charge les opérations multi-thread. La classe List maintiendra une variable modCount en interne pour enregistrer le nombre de modifications
Exemple : code source ArrayList

protected transient int modCount = 0;

Chaque fois qu'un Iterator est généré, l'Iterator enregistrera le modCount. Chaque fois que la méthode next() est appelée, l'enregistrement sera comparé au modCount de la classe externe List. S'il s'avère inégal, un. une exception d'édition multithread sera levée.

Pourquoi fais-tu ça ? Je crois comprendre que vous avez créé un itérateur étroitement couplé au contenu de la collection à parcourir, ce qui signifie que le contenu de la collection correspondant à cet itérateur est le contenu actuel. Je ne veux absolument pas le faire dans mon cas. tri à bulles. Quand , il y a encore des threads qui insèrent des données dans ma collection, n'est-ce pas ? Java utilise donc ce mécanisme de traitement simple pour empêcher la collection d'être modifiée pendant le parcours.

Quant à savoir pourquoi supprimer "1" est suffisant, la raison réside dans la méthode hasNext() de foreach et de l'itérateur. Le sucre syntaxique de foreach est en fait

.
while(itr.hasNext()){
    itr.next()
}

Donc, chaque boucle exécutera hasNext() en premier, alors jetez un œil à la façon dont hasNext() d'ArrayList est écrit :

public boolean hasNext() {
    return cursor != size;
}

cursor est une variable utilisée pour marquer la position de l'itérateur. La variable commence à 0 et effectue une opération +1 à chaque fois que next est appelé, donc :
Après que votre code supprime "1", size=1, curseur = 1, à ce moment hasNext() renvoie false et termine la boucle, donc votre itérateur n'appelle pas next pour trouver le deuxième élément, donc il n'y a aucun moyen de détecter modCount, donc il n'y aura pas d'exception de modification multi-thread
mais lorsque vous avez supprimé "2", l'itérateur a appelé next deux fois, à ce moment-là, size=1, curseur=2 et hasNext() a renvoyé true, donc l'itérateur a bêtement appelé next() à nouveau, ce qui a également déclenché If. modCount n'est pas égal, une exception de modification multithread sera levée.

Lorsque votre ensemble comporte trois éléments, vous constaterez comme par magie que la suppression de "1" lèvera une exception, mais la suppression de "2" ne posera pas de problème. La raison est liée à l'exécution du programme ci-dessus. L'ordre est cohérent. .

黄舟

Étant donné que le nombre dans la collection change lorsque vous ajoutez ou supprimez des éléments, des problèmes peuvent survenir lors du parcours. Par exemple, si une collection contient 10 éléments, elle doit être parcourue 10 fois. le nombre de traversées est incorrect, donc une erreur sera signalée

PHPzhong

Supprimez-les simplement dans l’ordre inverse. Quoi qu’il en soit, essayez de ne pas supprimer la liste. Vous pouvez ajouter une balise de suppression

伊谢尔伦

La description jaune dans le document est très intéressante.

Le résultat de l'exécution de cet exemple dépassera les attentes de tout le monde. Essayez donc de remplacer « 1 » par « 2 », cela aura-t-il le même résultat ?

Vous devez quand même regarder le code source de ArrayList pour cela, vous le saurez d'un coup d'œil.

巴扎黑

Supprimez-le simplement dans l'ordre inverse

Ty80

ArrayList n'est pas thread-safe, ce qui signifie que vous modifiez la liste pendant le parcours.
ArrayList lèvera une exception de modification simultanée dans ce cas.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal