Remplacez List <Optional <T >> par Optional <List <T >> en Java

TL;DR Version Java de la fonction de séquence et de la fonction de déplacement apparaissant dans "Conception et programmation du type de fonction Scala" Scala Functional Design & Programming - Un guide détaillé des fonctionnalités par Scalaz Contributor

Ce que tu veux faire (l'un d'entre eux)

Si vous recevez une liste de ʻOptional et que tous ont des valeurs (= isPresent est vrai), vous voulez faire une liste de valeurs sans ʻOptional. Si au moins une valeur est null (= isPresent est false), nous voulons retourner ʻOptional.empty`.

la mise en oeuvre

En gros, je pense que vous pouvez l'écrire comme ça. Si l'argument ou l'élément de liste contient null, une erreur se produira, mais cela n'est pas pris en compte ici. (C'est pire d'utiliser ~~ null ~~)

public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
    List<T> ys = new ArrayList<>();
    for (Optional<T> x : xs) {
        //S'il y a un élément qui n'a pas de valeur, le traitement est terminé à ce point
        if(x.isEmpty()) {
            return Optional.empty();
        }

        //Si la valeur existe, décompressez Facultatif et ajoutez-la à la liste
        ys.add(x.get());
    }

    //Enveloppe une liste de valeurs uniquement avec facultatif et la renvoie
    return Optional.of(ys);
}

Essayez d'utiliser

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(); //Liste vide

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

Cela semble fonctionner correctement.

application

Supposons que vous ayez la fonction suivante qui convertit une chaîne en nombre: Si la chaîne de caractères cible peut être convertie en une valeur numérique, cette fonction encapsule la valeur numérique dans ʻOptional et la renvoie. Si la conversion échoue, ʻOptional.empty est renvoyé. [^ 1]

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

Utilisez cette fonction pour convertir une liste de Strings en une liste de ʻOptional . De plus, si l'élément contient ʻOptional.empty, comme lors de l'application de la fonction de séquence, considérez une fonction qui rend également le résultat global ʻOptional.empty`. Nommez-le «traversée».

//Tout cela est convertible en nombres, donc facultatif[[1, 100, 30]]Vouloir
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"));

//Ceci est facultatif car il contient des valeurs qui ne peuvent pas être converties en nombres..Je veux le rendre vide
Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0", ""));

Cela pourrait être implémenté comme suit en utilisant la fonction de séquence ci-dessus.

public static Optional<List<Integer>> traverse(List<String> xs) {
    //Une fois facultatif<Integer>Convertir en une liste de
    List<Optional<Integer>> ys = new ArrayList<>();
    for (String x : xs) {
        Optional<Integer> o = safeParseInt(x);
        ys.add(o);
    }

    //Liste avec fonction de séquence<Optional<Integer>>Optionnel<List<Integer>>Conversion en
    return sequence(ys);
}

Si vous le déplacez, vous verrez que vous obtenez le résultat souhaité.

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

Cependant, ce qui précède est un peu inutile. Voir l'exemple suivant. La fonction travase est intégrée et sortie en fonction des résultats intermédiaires.

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

//C'est exactement le même traitement que la fonction travarse
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
    Optional<Integer> o = safeParseInt(x);
    ys.add(o);
}
Optional<List<Integer>> zs = sequence(ys);

//Sortie de la liste intermédiaire et des résultats
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

Le fait que zs devienne ʻOptional.empty est comme prévu. Cependant, cela a généré une liste intermédiaire «ys» qui est de la même longueur que «xs». C'est une décision d'appliquer la fonction safeParseInt au deuxième élément "" hoge "" de xs, et quand ʻOptional.empty est terminé, le résultat global sera également ʻOptional.empty. Il semble un peu inefficace de continuer le processus de conversion après avoir connu le résultat. Je ne pense pas que ce soit un problème si le nombre d'éléments est petit, mais si le nombre d'éléments est grand, cela peut affecter les performances. Lorsque ʻOptional.empty est atteint, la fonction safeParseInt n'est pas appliquée aux éléments après cela, et ʻOptional.empty` est renvoyé immédiatement.

Réécrivez la fonction travarse pour qu'elle le fasse.

public static Optional<List<Integer>> traverse(List<String> xs) {
    List<Integer> ys = new ArrayList<>();
    for (String x : xs) {
        //Convertir des chaînes en nombres
        Optional<Integer> o = safeParseInt(x);
    
        //Si la conversion échoue, le processus sera interrompu à ce stade.
        if(o.isEmpty()) {
            return Optional.empty();
        }

        //Si la conversion réussit, déballer Facultatif et ajouter à la liste
        ys.add(o.get());           
    }

    //Enveloppe une liste de numéros uniquement avec facultatif et la renvoie
    return Optional.of(ys);
}

J'ai arrêté d'utiliser la fonction de séquence et le processus de conversion et le branchement se font en une seule boucle. Maintenant, si la conversion échoue au milieu, ʻOptional.empty sera renvoyé immédiatement à ce stade. Faites de safeParseInt un paramètre pour que vous puissiez utiliser n'importe quelle fonction qui peut être convertie en ʻOptional.

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

Lorsque vous l'utilisez, passez-le en tant qu'expression lambda ou référence de méthode. En résumé, cela ressemble à ceci:

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) {
        //Style Lambda
        Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"), x -> safeParseInt(x));
        //Référence de la méthode
        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);
    }

//Plus utilisé
//    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 - réimplémentation de la fonction de séquence

J'ai arrêté d'appeler la fonction de séquence à partir de la fonction travarse, mais en fait, je peux utiliser cette nouvelle fonction travarse pour réimplémenter la fonction de séquence comme suit.

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

Cela peut être un peu étrange, mais ici Function.identity () (ou x-> x [^ 2]) est une fonction qui prend ʻOptional et renvoie ʻOptional <T>. , Le deuxième argument de Travarse Function <T, Optional <R >> f peut être écrit comme ci-dessus, tant que le type de retour est ʻOptional`. [^ 3]

Par souci d'explication, faisons correspondre le paramètre de type de la fonction travarse à cette nouvelle fonction de séquence.

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 est la fonction#Puisqu'il s'agit d'identité, x et o sont la même instance
        if(o.isEmpty()) {
            return Optional.empty();
        }
        ys.add(o.get()); //Après tout, c'est la même chose que d'emballer le contenu de x
    }
    return Optional.of(ys);
}

Comme pour la fonction de séquence définie au début, vous pouvez voir que nous venons de supprimer le Optional de chaque élément de List <Optional <T >>.

finalement

Je ne pense pas que Java définit des fonctions à usage général qui prennent ces fonctions comme arguments (c'est difficile à normaliser car il existe de nombreuses interfaces de type fonction), mais j'y ai pensé d'une manière ou d'une autre, alors je l'ai écrit. Mis à part son côté pratique, il est recommandé car il est amusant de faire des exercices cérébraux.

Merci d'avoir lu pour moi jusqu'à la fin. Si vous avez des questions ou des lacunes, veuillez nous contacter dans la section commentaires ou Twitter.

[^ 1]: facultatif est utilisé dans Optional Int afin de se connecter à une explication à usage général qui ne dépend pas du type de contenu de Optional. [^ 2]: Vous pouvez l'écrire comme x-> x, mais cette façon d'écrire générera un objet de typeFunction <T, T> ʻà chaque fois, donc à moins d'avoir une raison spécifique Je pense qu'il vaut mieux utiliserFunction.identity (). [^ 3]: Cela peut prêter à confusion car il est couvert par le paramètre de type T, mais ce que le travarse T et la séquence T` indiquent ici est un type différent. «T» de travarse correspond à «Optionnel » en séquence. Puisque les paramètres de type «T, R» de travarse peuvent être du même type, les types «Function <Optional et Optional >» satisfont aux exigences du deuxième argument de travarse.

Recommended Posts

Remplacez List <Optional <T >> par Optional <List <T >> en Java
Exemple de code pour convertir List en List <String> dans Java Stream
Changer le codage Java dans Windows
Agrégation de listes en Java (Collectors.groupingBy)
Java8 pour démarrer maintenant ~ Facultatif ~
[Java] Changez la langue et les paramètres régionaux en anglais avec les options JVM
Un moyen simple de vérifier la liste des méthodes / champs dans Java REPL
Multithread pour s'adapter au modèle [Java]
Comment apprendre JAVA en 7 jours
Enregistrer la sortie dans un fichier en Java
[Java] Convertir 1 en N liste en carte
Liste des membres ajoutés dans Java 9
[Java] Comment utiliser List [ArrayList]
Comment utiliser les classes en Java?
Comment nommer des variables en Java
Essayez d'implémenter Yuma en Java
Liste des types ajoutés dans Java 9
[Java] Conversion d'un tableau à une liste
Comment concaténer des chaînes avec Java
Convertir un tableau de chaînes en une liste d'entiers en Java
Comment implémenter le calcul de la date en Java
Comment implémenter le filtre de Kalman par Java
Prise en charge multilingue de Java Comment utiliser les paramètres régionaux
Comment changer le nom de l'application dans les rails
Essayez de résoudre Project Euler en Java
Facile à créer Slack Bot avec Java
Référence Java à comprendre dans la figure
[Java] Comment ajouter des données à la liste (add, addAll)
Comment faire une conversion de base en Java
Convertir des fichiers SVG en fichiers PNG en Java
Comment appliquer les conventions de codage en Java
Comment intégrer Janus Graph dans Java
Fonction de conversion d'objet de liste immuable (immuable) dans Java8
Comment obtenir la date avec Java
Différence entre les listes d'arry et les listes liées en Java
Ajouter des notes de bas de page aux documents Word en Java
Traitez n'importe quel nombre de cas dans la liste Java
[Java Bronze] 5 problèmes à garder à l'esprit
Exemple pour décompresser le fichier gz en Java
[Java] Découvrez comment utiliser correctement Optional
De Java à C et de C à Java dans Android Studio
Ne déclarez pas de variables dans List en Java
Ajouter l'attribut SameSite au cookie en Java
Comment obtenir une liste de noms de feuilles Excel en Java (POI vs SAX)
[Java] De deux listes à une liste de tableaux
Deux façons de démarrer un thread en Java + @
Je souhaite envoyer un e-mail en Java.
Comment afficher une page Web en Java
CORBA semble avoir été supprimé dans Java SE 11. .. ..
Java Type facultatif
Code pour échapper aux chaînes JSON en Java
Clonez la liste Java.
Essayez de créer un babillard en Java
Changements dans Java 11
[Java] Différentes méthodes pour acquérir la valeur stockée dans List par traitement itératif
Étudier Java 8 (facultatif)