Ändern Sie die Liste <Optional <T >> in Java in Optional <Liste <T >>

TL;DR Java-Version der Sequenzfunktion und der Traversenfunktion, die in "Scala Function Type Design & Programming" angezeigt wird Scala Functional Design & Programming - Eine ausführliche Anleitung zur Funktionsweise von Scalaz Contributor

Was Sie tun möchten (einer von ihnen)

Wenn Sie eine Liste mit "Optional" erhalten und alle Werte haben (= isPresent ist wahr), möchten Sie eine Liste mit Werten ohne "Optional" erstellen. Wenn auch nur ein Wert "null" ist (= isPresent ist falsch), möchte ich "Optional.empty" zurückgeben.

Implementierung

Ich denke, Sie können es ungefähr so schreiben. Wenn das Argument oder Listenelement null enthält, tritt ein Fehler auf, der hier jedoch nicht berücksichtigt wird. (Es ist schlimmer, ~~ null ~~ zu verwenden)

public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
    List<T> ys = new ArrayList<>();
    for (Optional<T> x : xs) {
        //Wenn es ein Element gibt, das keinen Wert hat, wird die Verarbeitung an diesem Punkt beendet.
        if(x.isEmpty()) {
            return Optional.empty();
        }

        //Wenn der Wert vorhanden ist, packen Sie Optional aus und fügen Sie ihn der Liste hinzu
        ys.add(x.get());
    }

    //Umschließt eine Liste mit nur Werten mit Optional und gibt sie zurück
    return Optional.of(ys);
}

Versuchen Sie es mit

List<Optional<String>>  xs = List.of(Optional.of("foo"), Optional.of("bar"));
List<Optional<Integer>> ys = List.of(Optional.of(1), Optional.empty(), Optional.of(100));
List<Optional<Boolean>> zs = List.of(); //Leere Liste

System.out.println(sequence(xs)); // Optional[[foo, bar]]
System.out.println(sequence(ys)); // Optional.empty
System.out.println(sequence(zs)); // Optional[[]]

Es scheint richtig zu funktionieren.

Anwendung

Angenommen, Sie haben die folgende Funktion, die eine Zeichenfolge in eine Zahl konvertiert: Wenn die Zielzeichenfolge in einen numerischen Wert konvertiert werden kann, umschließt diese Funktion den numerischen Wert mit "Optional" und gibt ihn zurück. Wenn die Konvertierung fehlschlägt, wird "Optional.empty" zurückgegeben. [^ 1]

public static Optional<Integer> safeParseInt(String s) {
    try {
        Integer n = Integer.valueOf(s);
        return Optional.of(n);
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

Verwenden Sie diese Funktion, um eine Liste von Strings in eine Liste von Optional <Integer> zu konvertieren. Wenn das Element "Optional.empty" enthält, z. B. beim Anwenden der Sequenzfunktion, ziehen Sie eine Funktion in Betracht, die auch das Gesamtergebnis "Optional.empty" ergibt. Nennen Sie es "durchqueren".

//Dies ist alles in Zahlen konvertierbar, also optional[[1, 100, 30]]Möchte
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"));

//Dies ist optional, da es Werte enthält, die nicht in Zahlen konvertiert werden können..Ich möchte es leer machen
Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0", ""));

Dies könnte unter Verwendung der obigen Sequenzfunktion wie folgt implementiert werden.

public static Optional<List<Integer>> traverse(List<String> xs) {
    //Einmal optional<Integer>In eine Liste von konvertieren
    List<Optional<Integer>> ys = new ArrayList<>();
    for (String x : xs) {
        Optional<Integer> o = safeParseInt(x);
        ys.add(o);
    }

    //Liste mit Sequenzfunktion<Optional<Integer>>Optional<List<Integer>>Umstellung auf
    return sequence(ys);
}

Wenn Sie es verschieben, sehen Sie, dass Sie das gewünschte Ergebnis erhalten.

System.out.println(traverse(List.of("1", "100", "30")));          // Optional[[1, 100, 30]]
System.out.println(traverse(List.of("1", "3", "hoge", "0", ""))); // Optional.empty

Das Obige ist jedoch etwas verschwenderisch. Siehe folgendes Beispiel. Die Travase-Funktion wird inline und basierend auf den Zwischenergebnissen ausgegeben.

List<String> xs = List.of("1", "hoge", "2", "3", "4", "5", "6", "7", "8", "9");

//Dies ist genau die gleiche Verarbeitung wie die Travarse-Funktion
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
    Optional<Integer> o = safeParseInt(x);
    ys.add(o);
}
Optional<List<Integer>> zs = sequence(ys);

//Zwischenliste und Ergebnisse ausgeben
System.out.println(ys); // [Optional[1], Optional.empty, Optional[2], Optional[3], Optional[4], Optional[5], Optional[6], Optional[7], Optional[8], Optional[9]]
System.out.println(zs); // Optional.empty

Die Tatsache, dass "zs" zu "Optional.empty" wird, ist wie erwartet. Dies hat jedoch eine Zwischenliste "ys" erzeugt, die dieselbe Länge wie "xs" hat. Es ist eine Entscheidung, die safeParseInt-Funktion auf das zweite Element "hoge" von "xs" anzuwenden, und wenn "Optional.empty" abgeschlossen ist, lautet das Gesamtergebnis auch "Optional.empty". Es fühlt sich etwas ineffizient an, den Konvertierungsprozess fortzusetzen, nachdem das Ergebnis bekannt ist. Ich denke nicht, dass es ein Problem ist, wenn die Anzahl der Elemente klein ist, aber wenn die Anzahl der Elemente groß ist, kann dies die Leistung beeinträchtigen. Ich möchte "Optional.empty" sofort zurückgeben, ohne die Funktion safeParseInt auf die Elemente anzuwenden, nachdem sie zu "Optional.empty" geworden sind.

Schreiben Sie die Travarse-Funktion so um, dass sie funktioniert.

public static Optional<List<Integer>> traverse(List<String> xs) {
    List<Integer> ys = new ArrayList<>();
    for (String x : xs) {
        //Konvertieren Sie Zeichenfolgen in Zahlen
        Optional<Integer> o = safeParseInt(x);
    
        //Wenn die Konvertierung fehlschlägt, wird der Prozess an diesem Punkt beendet.
        if(o.isEmpty()) {
            return Optional.empty();
        }

        //Wenn die Konvertierung erfolgreich ist, packen Sie Optional aus und fügen Sie sie der Liste hinzu
        ys.add(o.get());           
    }

    //Umschließt eine Liste mit nur Zahlen mit Optional und gibt sie zurück
    return Optional.of(ys);
}

Ich habe die Sequenzfunktion nicht mehr verwendet und sowohl der Konvertierungsprozess als auch die Verzweigung werden in einer Schleife ausgeführt. Wenn die Konvertierung in der Mitte fehlschlägt, wird "Optional.empty" sofort an diesem Punkt zurückgegeben. Machen Sie safeParseInt zu einem Parameter, damit Sie jede Funktion verwenden können, die in "Optional" konvertiert werden kann.

public static <T, R> Optional<List<R>> traverse(List<T> xs, Function<T, Optional<R>> f) {
    List<R> ys = new ArrayList<>();
    for (T x : xs) {
        Optional<R> o = f.apply(x);
        if(o.isEmpty()) {
            return Optional.empty();
        }
        ys.add(o.get());
    }

    return Optional.of(ys);
}

Übergeben Sie es bei Verwendung als Lambda-Ausdruck oder Methodenreferenz. Zusammenfassend sieht es so aus:

Main.java


import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        //Lambda-Stil
        Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"), x -> safeParseInt(x));
        //Methodenreferenz
        Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0"), Main::safeParseInt);

        System.out.println(xs); // Optional[[1, 100, 30]]
        System.out.println(ys); // Optional.empty
    }

    public static Optional<Integer> safeParseInt(String s) {
        try {
            Integer n = Integer.valueOf(s);
            return Optional.of(n);
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }

    public static <T, R> Optional<List<R>> traverse(List<T> xs, Function<T, Optional<R>> f) {
        List<R> ys = new ArrayList<>();
        for (T x : xs) {
            Optional<R> o = f.apply(x);
            if(o.isEmpty()) {
                return Optional.empty();
            }
            ys.add(o.get());
        }
        return Optional.of(ys);
    }

//Wird nicht mehr verwendet
//    public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
//        List<T> ys = new ArrayList<>();
//        for (Optional<T> x : xs) {
//            if(x.isEmpty()) {
//                return Optional.empty();
//            }
//            ys.add(x.get());
//        }
//        return Optional.of(ys);
//    }
}

Bonus - Implementierung der Sequenzfunktion

Ich habe aufgehört, die Sequenzfunktion über die Travarse-Funktion aufzurufen, aber tatsächlich kann ich diese neue Travarse-Funktion verwenden, um die Sequenzfunktion wie folgt neu zu implementieren.

public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
    return traverse(xs, Function.identity()); // traverse(xs, x -> x)Aber ja
}

Es mag etwas seltsam sein, aber hier ist "Function.identity ()" (oder "x-> x" [^ 2]) eine Funktion, die "Optional " verwendet und "Optional " zurückgibt. Das zweite Argument von Travarse "Funktion <T, Optional > f" kann wie oben geschrieben werden, solange der Rückgabetyp "Optional" ist. [^ 3]

Lassen Sie uns zur Erklärung den Typparameter der Travarse-Funktion mit dieser neuen Sequenzfunktion abgleichen.

public static <T> Optional<List<T>> traverse(List<Optional<T>> xs, Function<Optional<T>, Optional<T>> f) {
    List<T> ys = new ArrayList<>();
    for (Optional<T> x : xs) {
        Optional<T> o = f.apply(x); //f ist Funktion#Da es sich um Identität handelt, sind x und o dieselbe Instanz
        if(o.isEmpty()) {
            return Optional.empty();
        }
        ys.add(o.get()); //Immerhin ist es dasselbe wie das Packen des Inhalts von x
    }
    return Optional.of(ys);
}

Wie bei der zu Beginn definierten Sequenzfunktion können Sie sehen, dass wir gerade die Option von jedem Element von List <Optional <T >> entfernt haben.

Schließlich

Ich glaube nicht, dass Java Allzweckfunktionen definiert, die diese Funktionen als Argumente verwenden (es ist schwierig zu standardisieren, da es viele Schnittstellen vom Funktionstyp gibt), aber ich habe irgendwie darüber nachgedacht, also habe ich es geschrieben. Abgesehen von seiner Praktikabilität wird es empfohlen, weil es Spaß macht, Gehirnübungen zu machen.

Danke, dass Sie bis zum Ende für mich gelesen haben. Wenn Sie Fragen oder Mängel haben, kontaktieren Sie uns bitte im Kommentarbereich oder unter Twitter.

[^ 1]: Optional wird in Optional Int verwendet, um eine Verbindung zu einer allgemeinen Erklärung herzustellen, die nicht von der Art des Inhalts von Optional abhängt. [^ 2]: Sie können es wie "x-> x" schreiben, aber diese Schreibweise erzeugt jedes Mal ein Objekt vom Typ "Funktion <T, T>", es sei denn, Sie haben einen bestimmten Grund Ich denke, es ist besser, "Function.identity ()" zu verwenden. [^ 3]: Es mag verwirrend sein, weil es vom Typparameter "T" abgedeckt wird, aber was das travarse "T" und die Sequenz "T" hier zeigen, ist ein anderer Typ. T in travarse entspricht Optional <T> nacheinander. Da die Typparameter "T, R" von travarse vom gleichen Typ sein können, erfüllt der Typ "Function <Optional , Optional >" die Anforderung des zweiten Arguments von travarse.

Recommended Posts

Ändern Sie die Liste <Optional <T >> in Java in Optional <Liste <T >>
Beispielcode zum Konvertieren von List in List <String> in Java Stream
Ändern Sie die Java-Codierung in Windows
Listenaggregation in Java (Collectors.groupingBy)
Java8 startet jetzt ~ Optional ~
[Java] Ändern Sie Sprache und Gebietsschema mit JVM-Optionen in Englisch
Einfache Möglichkeit, die Methoden- / Feldliste in Java REPL zu überprüfen
Multithreaded passend zur [Java] Vorlage
Wie man JAVA in 7 Tagen lernt
Protokollausgabe in Datei in Java
[Java] Konvertiere 1 in N Liste in Karte
Liste der in Java 9 hinzugefügten Mitglieder
[Java] Verwendung von List [ArrayList]
Wie verwende ich Klassen in Java?
So benennen Sie Variablen in Java
Versuchen Sie, Yuma in Java zu implementieren
Liste der in Java 9 hinzugefügten Typen
[Java] Konvertierung von Array zu Liste
So verketten Sie Zeichenfolgen mit Java
Verwandeln Sie ein Array von Strings in eine Liste von Ganzzahlen in Java
So implementieren Sie die Datumsberechnung in Java
So implementieren Sie den Kalman-Filter mit Java
Mehrsprachige Unterstützung für Java Verwendung des Gebietsschemas
So ändern Sie den App-Namen in Rails
Versuchen Sie, Project Euler in Java zu lösen
Einfach, Slack Bot mit Java zu machen
Java-Referenz zum Verständnis in der Abbildung
[Java] Hinzufügen von Daten zur Liste (add, addAll)
So führen Sie eine Basiskonvertierung in Java durch
Konvertieren Sie SVG-Dateien in Java in PNG-Dateien
So erzwingen Sie Codierungskonventionen in Java
Einbetten von Janus Graph in Java
Unveränderlich (unveränderlich) Listet die Objektkonvertierungsfunktion in Java8 auf
So erhalten Sie das Datum mit Java
Unterschied zwischen Arrylist und verknüpfter Liste in Java
Fügen Sie Word-Dokumenten in Java Fußnoten hinzu
Verarbeiten Sie eine beliebige Anzahl von Fällen in Java List
[Java Bronze] 5 Probleme zu beachten
Beispiel zum Entpacken einer gz-Datei in Java
[Java] Erfahren Sie, wie Sie Optional richtig verwenden
Von Java nach C und von C nach Java in Android Studio
Deklarieren Sie keine Variablen in List in Java
Fügen Sie dem Cookie in Java das SameSite-Attribut hinzu
So erhalten Sie eine Liste mit Excel-Blattnamen in Java (POI vs SAX)
[Java] Von zwei Listen zu einer Array-Liste
Zwei Möglichkeiten, einen Thread in Java + @ zu starten
Ich möchte eine E-Mail in Java senden.
So zeigen Sie eine Webseite in Java an
CORBA scheint in Java SE 11 entfernt zu sein. .. ..
Java Optionaler Typ
Code zum Escapezeichen von JSON-Zeichenfolgen in Java
Klonen Sie die Java-Liste.
Versuchen Sie, ein Bulletin Board in Java zu erstellen
Änderungen in Java 11
[Java] Verschiedene Methoden, um den in List gespeicherten Wert durch iterative Verarbeitung zu ermitteln
Java 8 studieren (optional)