Comment éviter les exceptions dans les boucles for simples en JAVA ?
Introduction
Dans le développement réel d'un projet d'entreprise, tout le monde devrait être familier avec l'opération de suppression des éléments qui ne remplissent pas les conditions d'une liste donnée, n'est-ce pas ?
De nombreux étudiants peuvent immédiatement penser à de nombreuses façons d'y parvenir, mais ces méthodes de mise en œuvre auxquelles vous pensez sont-elles inoffensives pour les humains et les animaux ? De nombreuses opérations apparemment normales sont en réalité des pièges, dans lesquels de nombreux novices peuvent tomber s’ils ne font pas attention.
Si malheureusement vous marchez dessus :
Le code lancera directement une exception et signalera une erreur lors de l'exécution. C'est une bénédiction dans le malheur. . Au moins, vous pouvez le trouver à temps et le résoudre.
Le code s'exécute sans erreur, mais la logique métier présente inexplicablement divers problèmes étranges. , car si vous ne faites pas attention à ce problème, cela peut créer des dangers cachés pour les affaires ultérieures.
Alors, quelles sont les modalités de mise en œuvre ? Quelles méthodes de mise en œuvre peuvent poser problème ? Discutons-en ensemble ici. Veuillez noter que ce qui est discuté ici n'est pas la question de savoir comment écrire le mot « fenouil » dans les haricots de fenouil, mais un problème technique très sérieux et pratique qui est facilement ignoré.
Scénario de demande hypothétique :
Étant donné une liste d'utilisateurs allUsers, il est nécessaire de supprimer de la liste le personnel dont l'affiliation est dev, et de renvoyer les informations personnelles restantes #🎜🎜 #
Marcher sur les opérations de fosseméthode d'élimination des boucles foreachLa première pensée de nombreux novices est de vérifier les boucles for une par une, puis d'éliminer celles qui se rencontrent les conditions. ~ si facile...J'ai fini d'écrire le code en 1 minute :public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { for (UserDetail user : allUsers) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(user.getDepartment())) { allUsers.remove(user); } } // 返回剩余的用户数据 return allUsers; }
java.util.ConcurrentModificationException: null at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at com.veezean.demo4.UserService.filterAllDevDeptUsers(UserService.java:13) at com.veezean.demo4.Main.main(Main.java:26)
Je suis entré dans une fosse sans y prêter attention. Analysons pourquoi une exception est levée.
Analyse des causes :
Le traitement réel de la syntaxe foreach de JAVA est basé sur l'itérateur Iterator.
Au début de la boucle, une instance d'itération sera d'abord créée, et le ModCount attendu de cette instance d'itération se verra attribuer le modCount de la collection. Chaque fois que l'itérateur utilise hashNext() / next() pour parcourir l'élément suivant, il vérifiera si la variable modCount et la valeur attendueModCount sont égales. Si elles sont égales, le parcours sera renvoyé, sinon une ConcurrentModificationException sera levée. terminer le parcours.
Si vous ajoutez ou supprimez des éléments dans une boucle, vous appelez directement les méthodes add() et remove() de la collection, provoquant une augmentation ou une diminution du modCount, mais ces méthodes ne modifieront pas le ModCount attendu dans l'instance d'itération. Par conséquent, les valeurs de ExpectedModCount et modCount dans l'instance d'itération ne sont pas égales et une exception ConcurrentModificationException est levée.
Opération de boucle d'indice
Hmm ? Puisque la méthode foreach ne fonctionne pas, utilisez la méthode de boucle d'indice d'origine pour le faire. Vous n'obtiendrez pas d'erreur, n'est-ce pas ? C'est toujours très simple...
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { for (int i = 0; i < allUsers.size(); i++) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(allUsers.get(i).getDepartment())) { allUsers.remove(i); } } // 返回剩余的用户数据 return allUsers; }
Le code est terminé en une seule fois Exécutez-le et voyez le résultat traité :
{id=2, name. ='李思', département='dev'}{id=3, nom='王五', département='produit'}{id=4, nom='Tiezhu', département ='pm '}
Effectivement, aucune erreur n'a été signalée et le résultat a été affiché, parfait~
Attendez ? Est-ce vraiment bien ?
La logique de notre code est de juger si "dev".equals(department), mais dans le résultat de sortie, pourquoi y a-t-il encore des données comme département=dev qui devraient être éliminées ?
Si dans un projet d'entreprise réel, aucune erreur n'est signalée pendant la phase de développement et que les résultats ne sont pas soigneusement vérifiés, puis acheminés vers la chaîne de production, cela peut entraîner une logique commerciale anormale.
Revenons sur les raisons précises de ce phénomène.
Analyse des causes :
Nous savons qu'il n'y a en fait pas de relation de liaison forte entre les éléments de la liste et les indices, c'est juste une relation correspondante d'ordre de position, de liste Une fois l'élément du milieu modifié, l'indice correspondant à chaque élément peut changer, comme indiqué ci-dessous :
Ensuite, après avoir supprimé l'élément de la Liste, Les indices de tous les éléments après l'élément supprimé dans la liste sont avancés, mais le pointeur i de la boucle for est toujours accumulé vers l'arrière lors du traitement du suivant, certains éléments peuvent être manqués et ne pas être traités.
Par exemple, comme le montre la figure ci-dessous, lorsque i=0, il est jugé que l'élément A doit être supprimé, et il est supprimé directement lors du recyclage, i=1, à ce stade ; temps, parce que la position de l'élément dans la liste avance, l'élément B devient Il est devenu la position où l'indice d'origine était 0, ce qui a été directement manqué :
Donc ici vous pouvez savoir pourquoi le code ci-dessus Après l'exécution, les poissons passeront à travers le filet~
Voie correcte
Après avoir vu les deux pièges ci-dessus, que devrait être le mode de fonctionnement correct et approprié ?
Méthode itérateur
Eh ? C'est exact? Ne viens-je pas de dire que la méthode foreach utilise également des itérateurs, mais est-ce en fait une opération de piège ? Pourquoi est-il dit ici que le modèle d'itérateur est le bon chemin ?
虽然都是基于迭代器,但是使用逻辑是不一样的,看下代码:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { Iterator<UserDetail> iterator = allUsers.iterator(); while (iterator.hasNext()) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(iterator.next().getDepartment())) { // 这是重点,此处操作的是Iterator,而不是list iterator.remove(); } } // 返回剩余的用户数据 return allUsers; }
执行结果:
{id=3, name='王五', department='product'}
{id=4, name='铁柱', department='pm'}
这次竟然直接执行成功了,且结果也是正确的。为啥呢?
在前面foreach方式的时候,我们提过之所以会报错的原因,是由于直接修改了原始list数据而没有同步让Iterator感知到,所以导致Iterator操作前校验失败抛异常了。
而此处的写法中,直接调用迭代器中的remove()方法,此操作会在调用集合的remove(),add()方法后,将expectedModCount重新赋值为modCount,所以在迭代器中增加、删除元素是可以正常运行的。,所以这样就不会出问题啦。
Lambda表达式
言简意赅,直接上代码:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { allUsers.removeIf(user -> "dev".equals(user.getDepartment())); return allUsers; }
Stream流操作
作为JAVA8开始加入的Stream,使得这种场景实现起来更加的优雅与易懂:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { return allUsers.stream() .filter(user -> !"dev".equals(user.getDepartment())) .collect(Collectors.toList()); }
中间对象辅助方式
既然前面说了不能直接循环的时候执行移除操作,那就先搞个list对象将需要移除的元素暂存起来,最后一起剔除就行啦 ~
嗯,虽然有点挫,但是不得不承认,实际情况中,很多人都在用这个方法 —— 说的就是你,你是不是也曾这么写过?
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { List<UserDetail> needRemoveUsers = new ArrayList<>(); for (UserDetail user : allUsers) { if ("dev".equals(user.getDepartment())) { needRemoveUsers.add(user); } } allUsers.removeAll(needRemoveUsers); return allUsers; }
或者:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { List<UserDetail> resultUsers = new ArrayList<>(); for (UserDetail user : allUsers) { if (!"dev".equals(user.getDepartment())) { resultUsers.add(user); } } return resultUsers; }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

Guide du générateur de nombres aléatoires en Java. Nous discutons ici des fonctions en Java avec des exemples et de deux générateurs différents avec d'autres exemples.

Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

Guide de TimeStamp to Date en Java. Ici, nous discutons également de l'introduction et de la façon de convertir l'horodatage en date en Java avec des exemples.

Java est un langage de programmation populaire qui peut être appris aussi bien par les développeurs débutants que par les développeurs expérimentés. Ce didacticiel commence par les concepts de base et progresse vers des sujets avancés. Après avoir installé le kit de développement Java, vous pouvez vous entraîner à la programmation en créant un simple programme « Hello, World ! ». Une fois que vous avez compris le code, utilisez l'invite de commande pour compiler et exécuter le programme, et « Hello, World ! » s'affichera sur la console. L'apprentissage de Java commence votre parcours de programmation et, à mesure que votre maîtrise s'approfondit, vous pouvez créer des applications plus complexes.
