Heim  >  Artikel  >  Java  >  Wie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?

Wie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?

WBOY
WBOYnach vorne
2023-04-26 12:58:071041Durchsuche

Einführung

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.
  • Was sind also die Möglichkeiten, dies zu erreichen? Bei welchen Implementierungsmethoden können Probleme auftreten? Lassen Sie uns hier gemeinsam darüber diskutieren. Bitte beachten Sie, dass es hier nicht um die Frage geht, wie das Wort „Fenchel“ in Fenchelbohnen geschrieben wird, sondern um eine sehr ernste und praktische technische Frage, die leicht ignoriert wird.
Hypothetisches Bedarfsszenario:

Bei einer Benutzerliste mit allen Benutzern ist es erforderlich, das Personal, dessen Abteilung Entwickler ist, aus der Liste zu entfernen und die verbleibenden Personalinformationen zurückzugeben Die erste Idee besteht darin, die for-Schleife eine nach der anderen zu überprüfen und diejenigen zu eliminieren, die die Bedingungen erfüllen ~ so einfach...

Ich habe den Code in 1 Minute fertig geschrieben:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
    for (UserDetail user : allUsers) {
        // 判断部门如果属于dev,则直接剔除
        if ("dev".equals(user.getDepartment())) {
            allUsers.remove(user);
        }
    }
    // 返回剩余的用户数据
    return allUsers;
}

Dann habe ich selbstbewusst auf die Schaltfläche „Ausführen“ geklickt:

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)

eh? Was machst du? Warum wird die Ausnahme ausgelöst?

Ich bin in eine Grube geraten, ohne aufzupassen. Lassen Sie uns analysieren, warum eine Ausnahme ausgelöst wird.

Ursachenanalyse:

Die eigentliche Verarbeitung der foreach-Syntax von JAVA basiert auf dem Iterator Iterator.

Zu Beginn der Schleife wird zunächst eine Iterationsinstanz erstellt und dem erwarteten ModCount dieser Iterationsinstanz der modCount der Sammlung zugewiesen. Immer wenn der Iterator hashNext() / next() verwendet, um das nächste Element zu durchlaufen, prüft er, ob die modCount-Variable und der erwarteteModCount-Wert gleich sind. Wenn sie gleich sind, wird die Durchquerung zurückgegeben. Andernfalls wird eine ConcurrentModificationException ausgelöst Beenden Sie die Durchquerung.

Wenn Sie Elemente in einer Schleife hinzufügen oder löschen, rufen Sie direkt die Methoden add() und remove() der Sammlung auf, wodurch sich modCount erhöht oder verringert. Diese Methoden ändern jedoch nicht den erwarteten ModCount in der Iterationsinstanz, was zur Folge hat im erwarteten ModCount in der Iterationsinstanz. Wenn er nicht dem Wert von modCount entspricht, wird eine ConcurrentModificationException-Ausnahme ausgelöst.

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:

Wie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?{id=2, name='李思',partment='dev'}

{id =3, Name ='王五', Abteilung='Produkt'}

{id=4, Name='Tiezhu', Abteilung='pm'}

Natürlich wird kein Fehler gemeldet, das Ergebnis wird auch ausgegeben , perfekt~

etc. Ist das wirklich in Ordnung?

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.

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:

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:

Wie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?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-MethodeWie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?

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,所以在迭代器中增加、删除元素是可以正常运行的。,所以这样就不会出问题啦。

Wie vermeide ich Ausnahmen in einfachen for-Schleifen in JAVA?

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

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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen