question
J'ai besoin de supprimer des éléments non spécifiés de la première collection d'une collection de Java, basée sur le contenu d'une autre collection. Cela semble très simple, mais c'est un problème.
C'est le chef de la méthode que je veux écrire
ScreenblackNameList privé (Liste <SharedBoardsMswrapper> Source, List <BlackNamelistModel> BlackNamelist)
C'est ainsi que les choses sont. La collecte des sources enregistre certains éléments de données d'affichage. La collection BlackNamelist enregistre une liste de listes noires. Nous devons supprimer les données des utilisateurs sur liste noire dans la collection Source en fonction de la table de liste noire.
La solution à ce problème semble très simple.
J'utilise d'abord l'énoncé pour chaque instruction.
pour (SharedBoardsmswrapper tmpsharedboardsmswrapper: source) {for (BlackNamelistModel tmpBlackNamelistModel: BlackNamelist) {if (tmpsharedboardsmswrapper.getsource (). casser; }}}Question très simple! J'ai ri secrètement,
test…
Ce qui m'a surpris, c'est que ce code a en fait lancé une exception
java.util.ConcurrentModificationException。
Voir le manuel JDK6
classe publique concurrentModificationException, RuntimeException
Cette exception est lancée lorsque la méthode détecte la modification simultanée de l'objet mais ne permet pas une telle modification.
Par exemple, lorsqu'un fil itère sur une collection , une autre collection n'est pas autorisée à être modifiée linéairement. Habituellement, dans ces cas, les résultats de l'itération sont incertains. Si ce comportement est détecté, certaines implémentations d'Itérator (y compris toutes les implémentations de la collection communes fournies par JRE) peuvent choisir de lancer cette exception. L'itérateur qui effectue cette opération est appelé un itérateur de défaillance rapide car l'itérateur échoue très rapidement sans risquer le risque de comportement incertain arbitraire à un moment donné à l'avenir.
Notez que cette exception n'indique pas toujours que l'objet a été modifié simultanément par un thread différent. Si un seul thread émet une séquence d'appels de méthode qui viole le contrat de l'objet, l'objet peut lancer cette exception. Par exemple, si un thread modifie directement la collection lorsque les itérations de la collection à l'aide d'un itérateur Fast Fail, l'itérateur lancera cette exception.
Notez que le comportement de défaillance rapide de l'itérateur ne peut pas être garanti, car en général, il est impossible de garantir des garanties difficiles pour savoir s'il y a une modification simultanée hors synchrone. Une opération de défaillance rapide fera de son mieux pour lancer ConcurrentModificationException . Par conséquent, il est faux d'écrire un programme qui dépend de cette exception pour améliorer l'exactitude de ces opérations. La bonne façon est : ConcurrentModificationException doit être utilisée uniquement pour détecter les bogues.
For each de Java utilise réellement Iterator pour le traitement. L'itérateur ne permet pas de supprimer les collections pendant l'utilisation de l'itérateur . Et quand j'étais for each , j'ai supprimé un élément de la collection, ce qui a fait lancer ConcurrentModificationException conception en conception concurrente.
Il semble que nous ne puissions utiliser que le traditionnel pour la boucle honnêtement!
pour (int i = 0; i <source.size (); i ++) {SharedBoardsmswrapper tmpsharedboardsmswrapper = source.get (i); pour (int j = 0; j <BlackNamelist.size (); j ++) {BlackNamelistModel tmpBlackNameListModel = BlackNamelist.get (J); if (tmpsharedboardsmswrapper.getsource (). equals (tmpBlackNameListModel.getSource ())) {source.reMove (tmpsharedboardsmswrapper); casser; }}}Ça devrait aller maintenant! Appuyez sur le test avec confiance ...
s'évanouir! Que se passe-t-il? Comment les données peuvent-elles être mal filtrées?
Après le suivi du débogage , il a été constaté que lorsque l'ensemble supprime les éléments, la taille de l'ensemble deviendra plus petite et l'indice changera!
Que dois-je faire? Je ne serai pas impuissant à un si petit problème!
Utilisez Iterator pour supprimer les éléments de la collection
Vérifiez l'interface itérateur du manuel JDK et voyez qu'il a également une méthode de suppression .
Retirer
vide retire ()
Retirez le dernier élément renvoyé par l'itérateur de la collection pointé par l'itérateur (opération facultative). Cette méthode ne peut être appelée qu'une seule fois par appel. Si l'itérateur est modifié par une collection pointant par l'itérateur à l'aide d'une méthode autre que d'appeler cette méthode, le comportement de l'itérateur est incertain.
Lancer:
UnsupportedOperationException - Si l'itérateur ne prend pas en charge le fonctionnement de la suppression .
IllegalStateException - Si la méthode suivante n'a pas été appelée, ou si la méthode de suppression a été appelée après le dernier appel à la méthode suivante .
Code final correct:
/ ** * @ paramsource * @ paramblacknamelist * / privateVoid screenblackNameList (list <SharedBoardsMswrapper> source, list <BlackNamelistModel> BlackNamelist) {Iterator <SharedBoardsMswrapper> SourceIt = Source.iterator (); while (sourceIt.hasnext ()) {SharedBoardsMswrapper tmpsharedboardsmswrapper = sourceit.next (); Iterator <BlackNamelistModel> BlackNamelitiLtit = BlackNamelist.Itator (); while (BlackNamelilitTit.Hasnext ()) {BlackNameListModel tmpBlackNameListModel = BlackNamelitit.Next (); if (tmpsharedboardsmswrapper.getsource (). equals (tmpBlackNameListModel.getsource ())) {sourceIt.Remove (); casser; }}}}} Notez que la remove() next() d' Iterator ne peut pas être appelée plusieurs fois. Sinon, une exception sera lancée.
Il semble que le moyen le plus simple de supprimer les éléments d'une collection soit d'utiliser la méthode remove() d' Iterator !
Voyons comment l'itérateur fourni par la classe ArrayList est implémenté.
PrivateClass ITR implémente Iterator <e> {/ ** Il s'agit de l'index de l'élément, qui équivaut à un pointeur, ou curseur, qui l'utilise pour accéder à l'élément de données de la liste. * INDEXOFELENTAMENTTOBERETURDEDBYSUBSERSELENTCALLTONEXT. * / intCursor = 0; / ** * INDEXOFELEMENTSATRENDEDBYMOSTRECENTCALLTONEXTOR * Précédent. Resetto-1ifthisementeSdeletedByacall * toreMove. 最新元素的索引。 Si l'élément a été supprimé, réglé sur -1 * / intastret = -1; / ** Propriétés de la classe externe ArrayList: TRANSIGNAGE INT MODCOUNT = 0; Il est utilisé pour observer si le ArrayList est modifié par d'autres threads en même temps. S'il est incohérent, une exception synchrone sera lancée. * ThemodcountvalueThatheiterator aloeveresthat thebacking * listhouldhave. Si cette attente a été invitée, theiterator * a conçu la modification de la modification. * / IntexpectionModCount = modCount; // Si le curseur n'atteint pas la taille de la liste, il y a encore des éléments. publicBoolean Hasnext () {returnCursor! = size (); } // Renvoie l'élément actuel puis le curseur +1. Index récent = indice des éléments retournés. public e suivant () {checkForComodification (); essayez {e next = get (curseur); lastret = cursor ++; retour ensuite; } catch (indexoutofboundSexception e) {checkForComodification (); Thrownew NosuchementElementException (); }} / * Supprimer un élément, ce qui signifie supprimer l'élément actuel et mettre le curseur-1. Parce que la liste déplacera tous les éléments suivants dans le précédent. * / publicVoid dissovel () {if (lastret == -1) thrownew illégalStateException (); checkForComodification (); try {abstractList.this.remove (lastret); if (lastet <curseur) curseur--; lastret = -1; attendModCount = modCount; } catch (indexoutofboundSexception e) {thrownew concurrentModificationException (); }} finalVoid checkForComodification () {if (modCount! = attendModCount) thrownew concurrentModificationException (); }}Résumer
Comme vous pouvez le voir, Iterator supprime l'élément et réinitialise le curseur au siège correct. Tant qu'aucun autre thread ne change l'ensemble en même temps, il n'y aura pas de problème. Ce qui précède concerne cet article, j'espère qu'il sera utile à tout le monde d'apprendre Java.