Nutzen Sie entweder für die individuelle Ausnahmebehandlung in der Java Stream-API

Die Stream-API von Java ist sehr nützlich, aber die Ausnahmebehandlung ist langwierig.

Es gibt Möglichkeiten, es durch eine Laufzeitausnahme im Lambda zu ersetzen oder Optional zu verwenden, aber ich möchte etwas besser damit umgehen. Ich möchte Entweder als Monade verwenden, weil es eine große Sache ist. Im Moment ist entweder nicht standardmäßig implementiert, aber es scheint, dass dies mithilfe der Bibliothek vavr möglich ist. Es scheint, dass sich der Name der Bibliothek, die früher Javaslang hieß, geändert hat.

Entweder handelt es sich um einen Container, der wie Tupel zwei Elemente in Rechts / Links enthält. Wenn Sie einen Normalwert in Rechts und einen Fehler in Links halten, können Sie die Verarbeitung in einer Kette wie Optional verbinden. Optionale und beide Monaden aus Java8 Stream gelernt.

Daher möchte ich die Verwendung von Entweder mit der Stream-API organisieren.

Individuelle Ausnahmebehandlungsmuster in Stream

Was möchten Sie mit dieser Ausnahme tun, wenn Sie mit jedem Element des Streams etwas tun und innerhalb dieses Prozesses eine Ausnahme auftritt? Denken Sie vorerst an die folgenden drei Muster?

  1. Ich möchte alle Elemente ignorieren, die die Ausnahme ausgelöst haben, und nur die erfolgreichen Elemente extrahieren
  2. Ich möchte die erste Ausnahme abfangen
  3. Ich möchte alle aufgetretenen Ausnahmen abfangen

Wir werden überlegen, wie diese umgesetzt werden können.

0. Vorbereitung vorbereiten

Beispielcode

Hier ist ein Beispielcode, um über dieses Problem nachzudenken. Nimmt eine Zeichenfolgenliste als Argument und erstellt eine Ganzzahlliste mit Integer # parseInt dazwischen.

Test.java


public void test() {
    List<String> list = Arrays.asList("123", "abc", "45", "def", "789");

    List<Integer> result = list.stream()   //Mach es Stream
            .map(this::parseInt)           //Konvertieren Sie einzelne Elemente
            .collect(Collectors.toList()); //Zurück zur Liste als Kündigung
}

private Integer parseInt(String data) {
    try {
        return Integer.parseInt(value);
    } catch (Exception ex) {
        return null; //Ich drücke den Fehler und mache ihn null
    }
}

Dieses Beispiel drückt den Fehler und gibt also null zurück [123, null, 45, null, 789] Sie erhalten eine solche Liste.

Importieren Sie vavr

Halten Sie vavr verfügbar.

build.gradle


dependencies {
    ...
    implementation 'io.vavr:vavr:0.9.3'
}

1. Extrahieren Sie nur erfolgreiche Elemente

Beides wird nicht benötigt, da es den Fehler in diesem Fall nicht behandelt.

FilterTest.java


public void testFilterSuccess() {
    List<String> data = Arrays.asList("123", "abc", "45", "def", "789");

    // List<Integer>
    var result = data.stream()
            .map(this::parseIntOpt)
            .filter(Optional::isPresent).map(Optional::get)
            .collect(Collectors.toList());
}

private Optional<Integer> parseIntOpt(String value) {
    try {
        return Optional.of(Integer.parseInt(value));
    } catch (Exception ex) {
        return Optional.empty();
    }
}

Filtern Sie nur die Elemente mit Werten mit Filter und rufen Sie sie mit der Karte ab, um sie zu beenden.

2. Extrahieren Sie die erste Ausnahme

Das Hauptthema ist von hier.

Ändern Sie zunächst die Elementverarbeitung so, dass sie entweder zurückgibt.

EitherTest.java


private Either<Exception, Integer> parseIntEither(String value) {
    try {
        return Either.right(Integer.parseInt(value));
    } catch (Exception ex) {
        return Either.left(ex);
    }
}

Das Beispiel wurde geändert, um diese Funktion zu verwenden

EitherTest.java


public void testFirstException() {
    List<String> data = Arrays.asList("123", "abc", "45", "def", "789");

    // List<Either<Exception, Integer>>
    var result = data.stream()
            .map(this::parseIntEither)
            .collect(Collectors.toList()));
}

Zu diesem Zeitpunkt ist es noch eine Liste von Entweder. Wenn ein Fehler vorliegt, möchten wir den ersten Fehler extrahieren. Wenn dies normal ist, möchten wir "List " abrufen, damit wir eine weitere Konvertierung durchführen. Hier wird die in vavr implementierte statische Methode Entweder # sequenceRight verwendet.

EitherTest.java


// Either<Exception, Seq<Integer>>
var result = Either.sequenceRight(data.stream()
        .map(this::parseIntEither)
        .collect(Collectors.toList()));

Diese Methode wendet die Liste von Entweder in der Reihenfolge an, und wenn "Entweder.Links ()" vorhanden ist, behält sie ihren Wert bei, und wenn alles "Either.right ()" ist, aggregiert sie "Either.right" (Seq ) ) `Wird zurückgegeben. Sie müssen lediglich den Wert von Entweder abrufen.

EitherTest.java


try {
    List<Integer> intList = result.getOrElseThrow(ex -> ex).toJavaList();
    // handle intList
} catch (Exception ex) {
    // handle exception
}

Da Seq eine in vavr definierte Sequenzklasse ist, wird sie in eine Java-Liste konvertiert. Wenn jedoch kein Problem vorliegt, ist es in Ordnung, die Verarbeitung mit Seq fortzusetzen.

Zusammenfassend lautet der Code also wie folgt.

EitherTest.java


public void testFirstException() {
    List<String> data = Arrays.asList("123", "abc", "45", "def", "789");

    try {
        List<Integer> result = Either.sequenceRight(data.stream()
                .map(this::parseIntEither)
                .collect(Collectors.toList()))
                .getOrElseThrow(ex -> ex).toJavaList();
    } catch (Exception ex) {
        //Der folgende Fehler wird ausgelöst
        // For input string: "abc"
    }
}

Ich habe das Gefühl, dass ich es ganz ordentlich schreiben kann.

3. Extrahieren Sie alle auftretenden Ausnahmen

Es ist das gleiche wie 2. bis zur Mitte. Wenn Sie eine der beiden Sequenzen anstelle der beiden Sequenzen verwenden, werden alle Ausnahmen erfasst und Seq zurückgegeben.

EitherTest.java


// Either<Seq<Exception>, Seq<Integer>>
var result = Either.sequence(data.stream()
        .map(this::parseIntEither)
        .collect(Collectors.toList()));

Wenn Sie damit die gesammelte Ausnahmeliste mit Falz zusammenstellen, können Sie wie folgt schreiben.

EitherTest.java


public void testAllException() {
    List<String> data = Arrays.asList("123", "abc", "45", "def", "789");

    try {
        List<Integer> result = Either.sequence(data.stream()
                .map(this::parseIntEither)
                .collect(Collectors.toList()))
                .getOrElseThrow(exSeq -> exSeq.fold(new Exception("Multiple Exception"),
                        (root, value) -> {
                            root.addSuppressed(value);
                            return root;
                        }))
                .toJavaList();
    } catch (Exception ex) {
        // ex.getSuppressed()Zu"abc,"def"Ausnahme mit 2 Ausnahmen wird ausgelöst
    }
}

Jetzt können Sie auch die Ausnahmen für jedes Element aggregieren.

Extra: Ausnahmeliste bzw. Ganzzahlliste extrahieren (zusätzlich)

Entweder handelt es sich um eine Klasse, die immer entweder den Status Links oder Rechts annimmt. Wenn also eine Ausnahme vorliegt, ist sie isLeft (), andernfalls isRight (). Wenn es eine Ausnahme gibt, ist es daher nicht möglich, den Normalwert beim Extrahieren der Ausnahme zu erfassen. Ich bin mir nicht sicher, ob es eine solche Verwendung gibt, aber ich werde auch überlegen, wie man "List <Entweder <Ausnahme, Ganzzahl >>" für Studienzwecke in "Liste " und "Liste " trennt. ..

Der Ansatz besteht darin, Stream zu beenden

collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner)

Sammeln Sie zu Tuple2 <List <Ausnahme>, List <Integer >> von vavr mit.

TupleTest.java


//Tuple2<List<Exception>, List<Integer>>
var result = data.stream()
        .map(this::parseIntEither)
        .collect(() -> Tuple.of(new ArrayList<Exception>(), new ArrayList<Integer>()),
                (accumulator, value) -> {
                    if (value.isLeft()) {
                        accumulator._1.add(value.getLeft());
                    } else {
                        accumulator._2.add(value.get());
                    }
                },
                (left, right) -> {
                    left._1.addAll(right._1);
                    left._2.addAll(right._2);
                });

Es ist nicht sehr schön, wenn Sie so weit schreiben. .. Wenn Sie es wirklich implementieren möchten, möchten Sie möglicherweise eine separate Klasse erstellen, die die Collector-Schnittstelle implementiert.

** [Ergänzung] **

In den Kommentaren brachte er mir bei, wie man sehr einfach schreibt.

collect(Collectors.collectingAndThen(
  Collectors.groupingBy(Either::isLeft), 
  map -> Tuple.of(map.get(true),map.get(false))
);

Collectors.groupingBy gruppiert die Stream-Elemente nach dem angegebenen Prozess und gibt eine Map von (Grouping Key-> Element List) zurück. In diesem Fall erhalten wir "Map <Boolean, Seq <Entweder <Ausnahme, Ganzzahl >>>", indem wir gruppieren, ob entweder :: isLeft wahr oder falsch ist.

Darüber hinaus kann "Collectors.collectingAndThen" den Erfassungsprozess im ersten Argument und den nachfolgenden Prozess im zweiten Argument beschreiben. Das zweite Argument nimmt den Wert aus der Karte und konvertiert ihn in Tupel.

Wenn Sie es wie die anderen Beispiele organisieren, erhalten Sie den folgenden Code.

TupleTest2.java


var result = data.stream()
        .map(this::parseIntEither)
        .collect(Collectors.collectingAndThen(
                Collectors.groupingBy(Either::isLeft),
                map -> Tuple.of(
                        Either.sequence(map.get(true)).getLeft().toJavaList(),
                        Either.sequence(map.get(false)).get().toJavaList())
        ));

Der Code ist deutlich sauberer. Die Collectors-Methode ist praktisch, nicht wahr? Ich möchte es unten halten.

** [Ergänzung hier] **

Zusammenfassung

Ich hatte das Gefühl, dass die Ausnahmebehandlung von Stream, die ärgerlich war, recht ordentlich geschrieben werden konnte. Ich war eine Weile besorgt darüber, wie ich Entweder ordentlich organisieren sollte, aber natürlich wurde in vavr eine bequeme Methode implementiert. vavr bequem.

Referenz

Recommended Posts

Nutzen Sie entweder für die individuelle Ausnahmebehandlung in der Java Stream-API
Versuchen Sie es mit der Stream-API in Java
ChatWork4j für die Verwendung der ChatWork-API in Java
Java Stream API in 5 Minuten
Ausnahmebehandlungstechniken in Java
[java8] Um die Stream-API zu verstehen
Analysieren der COTOHA-API-Syntaxanalyse in Java
Rufen Sie die Windows-Benachrichtigungs-API in Java auf
Java-Ausnahmebehandlung?
Java Stream API
[Java] Ausnahmebehandlung
☾ Java / Ausnahmebehandlung
Informationen zur Behandlung von Java-Ausnahmen
Behandlung von Java-Ausnahmen
[Java] Behandlung von Java Beans in der Methodenkette
Versuchen Sie es mit der Syntaxanalyse der COTOHA-API in Java
Fragen in Java-Ausnahmebehandlung werfen und versuchen-fangen
[Java] Stream API / Map
Verwenden Sie Java-Lambda-Ausdrücke außerhalb der Stream-API
Fehlerprotokolle und Ausnahmebehandlung, die häufig im Java-Bereich angezeigt werden
Ein Hinweis zum Initialisieren von Feldern im Java-Lernprogramm
Zabbix API in Java
Ich habe [Klonmethode für Java-Arrays] als Klonmethode in Java-Arrays übersetzt.
[Ein Muss für einen Java-Ingenieurlehrling] Verwendung der Stream-API
Tipps zur Verwendung von Salesforce SOAP und Bulk API in Java
[Java] Praxis der Ausnahmebehandlung [Ausnahme]
Java Stream API Spickzettel
[Java] Informationen zur Behandlung von Try-Catch-Ausnahmen
[Ruby] Ausnahmebehandlung in Funktionen
Verwendungsregeln für die Behandlung von Java-Ausnahmen
[Java] Stream API - Stream-Beendigungsverarbeitung
[Java] Stream API - Stream Zwischenverarbeitung
[Java] Einführung in die Stream-API
Java-Anwendung für Anfänger: Stream
[Java] Stream API Zwischenoperation
Betrachten wir die Bedeutung von "Stream" und "Collect" in der Stream-API von Java.
Beispielcode zum Aufrufen der Yahoo! Local Search API in Java
Greifen Sie mit Java auf die Netzwerkschnittstelle zu
[Einführung in Java] Informationen zur Stream-API
[Inhouse-Studiensitzung] Behandlung von Java-Ausnahmen (26.04.2017)
Geben Sie den Java-Speicherort in eclipse.ini an
Entpacken Sie die Zip-Datei in Java
Generieren Sie die CloudStack-API-URL in Java
Treffen Sie Zaims API (OAuth 1.0) in Java
Ich habe versucht, die Java8 Stream API zu verwenden
Einstellungen für das SSL-Debugging in Java
Tipps zum Umgang mit Enum mit Thymeleaf
JPA (Java Persistence API) in Eclipse
Verwenden Sie Stream in Java?
Tipps zum Umgang mit Pseudoelementen in Selen
Rufen Sie die Super-Methode in Java auf
Ich habe die COTOHA-API-Syntaxanalyse 100 Mal in Java aufgerufen, um die Leistung zu messen.
[Socket-Kommunikation (Java)] Eindrücke von der erstmaligen Implementierung der Socket-Kommunikation in der Praxis
[Java] Generieren Sie mithilfe der Stream-API eine verengte Liste aus mehreren Listen
Ihnen, denen im Feld "Keine Stream-API verwenden" mitgeteilt wurde
Erste Programmierung in meinem Leben Java 1st Hello World