Bei der tatsächlichen Entwicklung von Geschäftsprojekten sollte jeder mit der Vorgehensweise zum Entfernen von Elementen, die die Bedingungen nicht erfüllen, aus einer bestimmten Liste vertraut sein, oder?
Viele Studierende fallen sofort auf viele Umsetzungsmethoden ein, aber sind diese Umsetzungsmethoden Ihrer Meinung nach ungefährlich für Mensch und Tier? Viele scheinbar normale Vorgänge sind in Wirklichkeit Fallen, und viele Anfänger könnten in sie tappen, wenn sie nicht vorsichtig sind.
Wenn Sie versehentlich darauf treten:
Der Code löst beim Ausführen direkt eine Ausnahme aus und meldet einen Fehler. Zumindest kann er rechtzeitig entdeckt und behoben werden
Der Code meldet keinen Fehler, aber die Geschäftslogik erscheint aus unerklärlichen Gründen. Diese Art ist tragischer, denn wenn Sie diesem Problem keine Aufmerksamkeit schenken, kann es zu versteckten Gefahren für spätere Geschäfte kommen.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)
Indexschleifenoperation
Hmm? Da die foreach-Methode nicht funktioniert, verwenden Sie dazu die ursprüngliche Subscript-Loop-Methode. Sie erhalten keine Fehlermeldung, oder? Es ist immer noch sehr einfach...
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; }
Der Code ist auf einmal fertig, führen Sie ihn aus und sehen Sie sich die verarbeitete Ausgabe an:
{id=2, name='李思',partment='dev'}
{id =3, Name ='王五', Abteilung='Produkt'}Werfen wir einen Blick auf die konkreten Gründe für dieses Phänomen. Ursachenanalyse: Wir wissen, dass es tatsächlich keine starke Bindungsbeziehung zwischen den Elementen in der Liste und den Indizes gibt. Es handelt sich lediglich um eine entsprechende Beziehung der Positionsreihenfolge der Elemente in der Liste Die Indizes können sich wie folgt ändern:Unsere Codelogik besteht darin, zu beurteilen, ob „dev“.equals(department) ist, aber warum gibt es im Ausgabeergebnis immer noch Daten wie „department=dev“, die entfernt werden sollten?
Wenn es sich um ein echtes Geschäftsprojekt handelt, während der Entwicklungsphase keine Fehler gemeldet werden und die Ergebnisse nicht sorgfältig überprüft werden und dann in die Produktionslinie gelangen, kann dies zu einer abnormalen Geschäftslogik führen.
Nach dem Löschen eines Elements aus der Liste werden die Indizes aller Elemente hinter dem gelöschten Element in der Liste nach vorne verschoben, aber der Zeiger i des for Schleife akkumuliert immer rückwärts. Ja, bei der Verarbeitung der nächsten Schleife werden möglicherweise einige Elemente übersehen und nicht verarbeitet.
Wie in der Abbildung unten gezeigt, wird beispielsweise bei i = 0 beurteilt, dass das A-Element gelöscht werden muss, und beim Recycling wird es zu diesem Zeitpunkt direkt gelöscht, da die Position von Das Element in der Liste bewegt sich vorwärts, das B-Element wird zum ursprünglichen Index: Die Position 0 wird direkt übersehen:
Hier können Sie also erfahren, warum der obige Code nach der Ausführung übersehen wird ~
Der richtige Weg
Ich habe die oben genannten 2 Fallstricke gesehen. Was sollte nach der Operation die richtige und angemessene Operationsmethode sein?
Iterator-Methode
eh? Das stimmt? Habe ich nicht gerade gesagt, dass die foreach-Methode auch Iteratoren verwendet, aber handelt es sich tatsächlich um eine Trap-Operation? Warum heißt es hier, dass das Iteratormuster der richtige Weg ist?虽然都是基于迭代器,但是使用逻辑是不一样的,看下代码:
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,所以在迭代器中增加、删除元素是可以正常运行的。,所以这样就不会出问题啦。
言简意赅,直接上代码:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { allUsers.removeIf(user -> "dev".equals(user.getDepartment())); return allUsers; }
作为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; }
Das obige ist der detaillierte Inhalt vonWie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!