Pourquoi Alibaba est interdite dans l'élément de boucle foreach dans le supprimer / ajouter des opérations

Manuel Alibaba Java Developer, il est prévu d'un tel:

Mais le manuel ne donne pas une raison spécifique, nous venons d'analyser en profondeur la pensée derrière cette disposition.

boucle foreach

boucle Foreach (boucle Foreach) est une des instructions en langage de programmation informatique en un flux de commande, typiquement utilisés pour une boucle à travers le réseau ou ensemble d'éléments.

langage Java, JDK 1.5.0 depuis le début de l'introduction de la boucle foreach. Dans à travers le réseau, l'aspect collection, foreach fournir une grande commodité pour les développeurs.

syntaxe foreach est la suivante:

pour (variable élément du type élément T x: traverser l'objet obj) { Java a cité la déclaration de x; }

Les exemples suivants illustrent commun pour boucle foreach et recyclage:

public static void main (String args) { // initialiser une liste à l'aide ImmutableList liste < chaîne >  USERNAMES = ImmutableList.of ( "Hollis", "hollis", "HollisChuang", "H"); System.out.println ( "Utilisation boucle for itère List"); pour (int i = 0; i <  userNames.size (); i ++) { System.out.println (userNames.get (i)); } System.out.println ( "en utilisant foreach pour parcourir la liste"); pour (String userName: noms d'utilisateur) { System.out.println (nom d'utilisateur); } }

Code de sortie est des résultats run:

Utilisation de la boucle itère Liste Hollis Hollis HollisChuang H En utilisant foreach pour parcourir la liste Hollis Hollis HollisChuang H

On peut voir en utilisant une boucle foreach par la syntaxe de collection ou un tableau, vous pouvez jouer le même effet et pour la circulation générale, et le code plus concis. Ainsi, la boucle foreach est aussi communément également fait référence amélioré pour la boucle.

Cependant, en tant que programmeurs qualifiés, il faut savoir non seulement ce qui est améliorée pour la boucle, vous avez aussi besoin de savoir ce que le principe est renforcé pour la boucle?

En fait, la boucle renforcée est un sucre syntaxique pour Java nous fournit, si le fichier de classe après le code ci-dessus est compilé à décompiler (outil d'utilisation JAD), vous pouvez obtenir le code suivant:

Iterator itérateur = userNames.iterator (); faire { if (! iterator.hasNext ()) break; Chaîne UserName = (String) iterator.next (); si (userName.equals ( "Hollis")) userNames.remove (nom d'utilisateur); } While (true); System.out.println (noms d'utilisateur);

Peut constater que la boucle améliorée, la boucle while en fait de la mise en uvre dépendante et Iterator. (Gardez à l'esprit que cette mise en uvre sera utilisé plus tard!)

Pour reproduire le problème

Etats Spécification ne nous permettent pas d'ajouter / les opérations de suppression sur l'ensemble des éléments dans une boucle foreach, nous essayons de le faire et de voir ce qui se passera.

// utilisent le double syntaxe entre parenthèses (syntaxe double accolade) établir et initialiser une liste liste < chaîne >  noms_utilisateurs = new ArrayList < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; pour (int i = 0; i <  userNames.size (); i ++) { si (userNames.get (i) equals ( "Hollis")) { userNames.remove (i); } } System.out.println (noms d'utilisateur);

Le code ci-dessus, la première double de parenthèses (la syntaxe double entretoise) à établir et à initialiser une liste, qui contient les quatre cordes, respectivement, Hollis, Hollis, HollisChuang et H.

Ensuite, utilisez ordinaire boucle pour parcourir la liste, supprimer le contenu de l'élément de liste égal aux éléments Hollis. Ensuite, la liste de sortie, les résultats de sortie sont les suivantes:

Ce qui est plus courant d'utiliser une boucle pour supprimer tout déplacement, nous regardons, si vous utilisez la boucle améliorée, alors ce qui se passe:

liste < chaîne >  noms_utilisateurs = new ArrayList < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; pour (String userName: noms d'utilisateur) { si (userName.equals ( "Hollis")) { userNames.remove (nom d'utilisateur); } } System.out.println (noms d'utilisateur);

Le code ci-dessus, en utilisant une boucle renforcée par les éléments, et essayer de supprimer des éléments de chaîne Hollis dans celui-ci. Exécutez le code ci-dessus jetteront l'exception suivante:

java.util.ConcurrentModificationException

De même, le lecteur peut essayer d'ajouter des éléments dans la boucle améliorée en utilisant la méthode d'ajout, seront jetés les mêmes résultats.

La raison de cette anomalie se produit parce que déclenche un mécanisme de détection d'erreur une collection Java --fail rapide.

fail-rapide

Ensuite, nous devons analyser les raisons au moment amélioré pour ajouter la boucle / supprimer des éléments jette java.util.ConcurrentModificationException, l'explication suivante à la fin qui est ce qui est binaire fail-rapide, l'échec rapide principe et ainsi de suite.

fail-rapide, l'échec rapide qui est, il est un ensemble de mécanisme de détection d'erreur Java. Quand un ensemble d'une pluralité de fils (non-fail-safe classe de collection) sur l'opération de modification de la structure, il peut y avoir des mécanismes défaillance rapide, qui est renvoyée lorsqu'une ConcurrentModificationException (modification simultanée lorsque l'objet détecté, mais hors de l'anomalie ne permet pas une telle modification).

Notez également que, sinon environnement multi-thread, si un seul thread viole les règles, il y a aussi susceptibles de changer lancer une exception.

Eh bien, élémentaire delete amélioré pour la boucle, est une violation des règles de la façon de faire?

Pour analyser cette question, nous avons d'abord amélioré pour la boucle ce sucre syntaxique de sucre, donner le code suivant:

public static void main (String args) { // initialiser une liste à l'aide ImmutableList liste < chaîne >  noms_utilisateurs = new ArrayList < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; Iterator itérateur = userNames.iterator (); faire { if (! iterator.hasNext ()) break; Chaîne UserName = (String) iterator.next (); si (userName.equals ( "Hollis")) userNames.remove (nom d'utilisateur); } While (true); System.out.println (noms d'utilisateur); }

Et puis exécutez le code ci-dessus, il lancera une exception. Nous regardons la pile complète de ConcurrentModificationException:

par la pile d'exception, nous pouvons aller, la ligne 23 chaîne d'appel exception ForEachDemo se produit, Iterator.next appeler la méthode Iterator.checkForComodification, mais une exception est levée méthode checkForComodification.

En fait, après mise au point, nous pouvons voir que, si le code de suppression n'est pas exécuté, a iterator.next cette ligne n'est pas donnée. Lancers supprimer le moment est exactement appel qu'une méthode suivante après l'exécution.

Nous regardons le code checkForComodification méthode directe, un regard jette la cause de l'anomalie:

finale checkForComodification void () { si (modCount! = expectedModCount) throw new ConcurrentModificationException (); }

Le code est relativement simple, modCount! = ExpectedModCount et ils jeter un ConcurrentModificationException.

Ensuite, regardez, supprimer / ajouter salle d'opération et comment conduire modCount expectedModCount pas égale.

retirer / ajouter ce qui a été fait

Tout d'abord, nous devons comprendre est, à la fin de ces deux variables modCount et expectedModCount sont, quel genre de choses.

En tournant le code source, nous pouvons trouver:

  • modCount est une variable membre dans la liste de tableaux. Il indique le nombre de fois défini est réellement modifié.
  • expectedModCount est une variable membre d'une classe ArrayList interne dans --Itr. expectedModCount indique le numéro de l'ensemble est modifiée dans cette itérateur souhaitée. La valeur est dans le temps ArrayList.iterator méthode est appelée initialisation. La manipulation de la collection à travers le iterator, la valeur reste inchangée.
  • ITR est d'atteindre un itérateur en utilisant des méthodes ArrayList.iterator obtenu iterator est une instance de la classe ITR.

La relation entre eux est la suivante:

classe ArrayList { int modCount privé; public void add (); supprimer public void (); classe privée Itr de Iterator < E >  { int expectedModCount = modCount; } publique Iterator < E >  iterator () { return new Itr (); } }

En fait, je vois ici, beaucoup de gens peut probablement deviner comment après supprimer / ajouter des opérations, et conduira à expectedModCount modCount ne veulent pas attendre.

Lisez le code, on peut aussi trouver, supprimer la méthode logique de base est la suivante:

On peut le voir, il a seulement changé modCount, et ne rien faire pour expectedModCount.

En bref résumé la raison ConcurrentModificationException va lancer une exception, parce que notre code à l'aide d'une boucle améliorée, alors que la boucle améliorée, la collection est traversée par l'itérateur, mais les éléments du add / remove est utilisé directement collections façon. Cela se traduit par l'itérateur traversal, vous constaterez qu'il ya un élément à supprimer / ajouté dans le cas de leur propre, sans le savoir, va lancer une exception, utilisée pour demander à l'utilisateur, des modifications simultanées ont pu se produire!

Une bonne posture

Nous avons jusqu'à présent décrit clairement la raison de la collecte ne peut pas ajouter / supprimer intervenant directement dans le corps de la boucle foreach.

Cependant, de nombreuses fois, nous avons un ensemble d'exigences à filtrer, par exemple dans le cadre de suppression de l'élément, alors comment? Il existe plusieurs méthodes disponibles pour référence:

1, agit directement itérateur

En plus de l'utilisation directe de l'ordinaire pour la boucle, on peut également utiliser la méthode de suppression Iterator fournissent directement.

 liste < chaîne >  noms_utilisateurs = new ArrayList < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; Iterator itérateur = userNames.iterator (); while (iterator.hasNext ()) { si (iterator.next (). equals ( "Hollis")) { Iterator.remove (); } } System.out.println (noms d'utilisateur);

Si la méthode directe en utilisant le supprimer fourni Iterator, il peut être modifié à une valeur de expectedModCount. Ensuite, il ne sera pas jeté vers le haut. Quels sont les codes sont les suivants:

2, en utilisant la Java 8 prévu dans le filtre de filtrage

Java 8 peut être converti en un ensemble de flux, il y a un filtre pour l'opération de débit, peut être testé pour un flux d'origine, il est laissé pour générer un nouveau test élémentaire Stream.

 liste < chaîne >  noms_utilisateurs = new ArrayList < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; . USERNAMES = filtre userNames.stream () (Username - >  ! UserName.equals ( "Hollis")) Collect (Collectors.toList ()) .; System.out.println (noms d'utilisateur);

3, en utilisant la boucle de renforcement, en fait,

Si nous sommes très déterminés dans un ensemble, une sur le point de supprimer l'élément contient uniquement un mot, comme pour l'opération Set, donc en fait peut utiliser la boucle améliorée, longtemps après la suppression, la boucle se termine immédiatement, ne continuera pas de le traverser, cela ne veut pas l'exécution de code à l'autre méthode suivante.

 liste < chaîne >  noms_utilisateurs = new ArrayList < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; pour (String userName: noms d'utilisateur) { si (userName.equals ( "Hollis")) { userNames.remove (nom d'utilisateur); break; } } System.out.println (noms d'utilisateur);

4, l'utilisation directe des collections fail-safe

En Java, en plus de certaines classes de collecte communes, il y a quelques utilisations des collections de sécurité ne mécanisme. Ce conteneur de collecte ne sont pas directement accessibles sur le contenu de la collection lors de la traversée, mais la première copie du contenu original de la collection, à parcourir sur une copie de la collection.

Parce que la copie originale de l'itération est réglée sur traverse, de sorte que lors de la traversée des modifications apportées à l'ensemble d'origine ne doivent pas être détectées par l'itérateur ne sera pas déclenchée ConcurrentModificationException.

ConcurrentLinkedDeque < chaîne >  noms_utilisateurs = nouveau ConcurrentLinkedDeque < chaîne > () {{ ajouter ( "Hollis"); ajouter ( "Hollis"); ajouter ( "HollisChuang"); ajouter ( "H"); }}; pour (String userName: noms d'utilisateur) { si (userName.equals ( "Hollis")) { userNames.remove (); } }

Copier le contenu est basé sur les avantages d'éviter ConcurrentModificationException, mais encore une fois, l'itérateur n'a pas accès au contenu modifié, à savoir: iterator traversal est de commencer par la collecte pour obtenir une copie du moment, au cours de la traversée de l'ensemble des modifications d'origine Happen iterator est inconnue.

Les conteneurs sont en sécurité dans le cadre du paquet java.util.concurrent échoue, vous pouvez utiliser une concurrente multithread, les modifications concurrentes.

Or cochon d'or tirés des prix! TVB « Missy » Happy Thanksgiving: Il y a de l'espoir si longtemps
Précédent
États à la qualification de la production automobile Iris Chang a remporté les 10 premières voitures électriques
Prochain
Gossip physique! Âgé de 30 ans TVB supérieur avec fleurons « derrière le joueur » surge explosive popularité scène pleurer
« La technologie noire » ruine la Coupe du Monde? Donnez-moi une pause ......
Étoiles à pied | Bryant à pied ultra-limitée de 10 paires de chaussures, patauger à pied interprétation « Rekindle » du vertueux!
« Les entreprises centrales à micro-histoire de la réduction de la pauvreté » son « Ahmat chaud », explique la responsabilité et le jeu
Guangzhou Automobile nouvelle Acura RDX mis en vente 32,80 à 43,80 dix mille yuans
Aucun grand publique micro-canal de révision: l'avenir de tous les pousser à la fois la vie et la mort
Un enfant ramasser « ferraille » vend maintenant dans le tableau périodique par millions
décompression de vitesse! frère était un spécialiste TVB dans la transition réussie dans la ligne 18 a gagné peu choyé
Wang Yibo même paragraphe édition limitée pas à la main lente! Converse x JW Anderson Grille coming soon!
aimer les autres! 48 ans actrice TVB unique depuis de nombreuses années dépêchez pas de se marier: Je ne veux pas se marier et se marier
Guangzhou Automobile Mitsubishi chansons Wilson mis en vente 12,98 à 18,58 dix mille yuans
Commandez la livraison gratuite? ! McDonald frites vêtements burger ensemble limité de paquets ne viennent pas?