[JAVA] L'histoire de Collectors.groupingBy que je veux garder pour la postérité

introduction

Le titre est génial, mais Collectors.groupingBy est juste une histoire formidable.

Récemment, il y a eu de nombreux cas de création de diverses cartes en utilisant groupingBy (pour une raison quelconque, il y a eu peu de cas de création de cartes, donc je ne les ai pas beaucoup utilisées). Cet article est un résumé de ce qui a été étudié à ce moment-là.

environnement

Java10 Lombok

Regroupement par une simple touche

L'avantage de Collectors.groupingBy est que vous pouvez facilement les agréger en leur donnant une clé. Si vous souhaitez modifier la valeur de Map, vous pouvez ajouter un autre argument et l'écrire ici, afin qu'il prenne en charge l'agrégation dans divers cas.

List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,1,1,1);
Map<Integer,List<Integer>> map = nums.stream().collect(Collectors.groupingBy(i ->i));
System.out.println(map);
=> {1=[1, 1, 1, 1], 2=[2], 3=[3], 4=[4], 5=[5], 6=[6], 7=[7], 8=[8], 9=[9]}

Regroupement par plusieurs touches

Cependant, la plus grande caractéristique de groupingBy est qu'il fonctionne bien même si vous définissez des clés compliquées. La définition de plusieurs clés est très facile, il suffit de passer plusieurs champs lors de la spécification des clés. "-" Est ajouté pour faciliter la visualisation, mais il n'y a pas de problème.

En tant que clé cette fois, nom1 et nom2 seront définis comme clés.

    public static void main(String[] arg) {
        List<String> names = List.of("taro", "jiro", "saburo");

        List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        
        Map<String,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2()));
        System.out.println(map);
        // => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro)]}
    }

    @Data
    private static class Name {
        private String name1;
        private String name2;
        private String name3;

        public Name(String name1, String name2, String name3) {
            this.name1 = name1;
            this.name2 = name2;
            this.name3 = name3;
        }
    }

Avec cela seul, nom3 a la même valeur, vous ne pouvez donc pas dire si nom1 et nom2 sont les clés. Je voudrais donc ajouter nouveau nom (" taro "," jiro "," taro ") à la liste et voir quel type de carte peut être créé.

    public static void main(String[] arg) {
        List<String> names = List.of("taro", "jiro", "saburo");

        List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name("taro", "jiro", "taro"));

        Map<String,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2()));
        System.out.println(map);
        // => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro)], taro-jiro=[Main.Name(name1=taro, name2=jiro, name3=taro)]}
    }

Comme vous pouvez le voir dans le résultat, l'objet ajouté cette fois est indépendant car taro-jiro = [Main.Name (name1 = taro, name2 = jiro, name3 = taro)], donc name1 et name2 sont Vous pouvez voir que c'était la clé.

Ce qui est inquiétant ici, c'est que le nom résultant3 contient "taro". L'avantage de groupingBy est qu'il fait la même chose pour les valeurs de champs autres que ceux spécifiés dans la clé, même si elles sont identiques aux autres champs.

Je voudrais courir avec name1 et name3 comme clés pour vérifier si c'est vrai.

    public static void main(String[] arg) {
        List<String> names = List.of("taro", "jiro", "saburo");

        List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name("taro", "jiro", "taro"));

        Map<String,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName3()));
        System.out.println(map);
        // => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro), Main.Name(name1=taro, name2=jiro, name3=taro)]}
    }

Lorsque vous vérifiez le résultat de l'exécution, l'objet ajouté comme nouveau nom (" taro "," jiro "," taro ") ʻis taro-taro = [Main.Name (nom1 = taro, nom2 = taro, nom3 = taro) ), Main.Name (name1 = taro, name2 = jiro, name3 = taro)] ʻet est enregistré comme la valeur de la clé de taro-taro comme prévu.

Jusqu'où peuvent aller plusieurs clés?

J'ai changé la commande

Regardons un autre modèle d'exemples name1 et name3. Vous pouvez voir les champs jusqu'à l'exemple précédent, mais regardons également l'exemple où nom1 et nom3 sont échangés.

    public static void main(String[] arg) {
        List<String> names = List.of("taro", "jiro", "saburo");

        List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name("taro", "jiro", "taro"));
        nameList.add(new Name("jiro", "taro", "taro"));

        Map<String,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName3()));
        System.out.println(map);
        // => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], jiro-taro=[Main.Name(name1=jiro, name2=taro, name3=taro)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro), Main.Name(name1=taro, name2=jiro, name3=taro)]}
    }

Il semble que même si vous modifiez l'ordre, il répondra correctement.

J'ai essayé avec Integer

J'ai essayé l'exemple précédent avec le type Integer.

    public static void main(String[] arg) {
        List<Integer> nums = List.of(1,2,3);

        List<Name> nameList = nums.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name(1, 2, 1));
        nameList.add(new Name(2, 1, 1));

        Map<Integer,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + it.getName2()));
        System.out.println(map);
    }

    @Data
    private static class Name {
        private Integer name1;
        private Integer name2;
        private Integer name3;

        public Name(Integer name1, Integer name2, Integer name3) {
            this.name1 = name1;
            this.name2 = name2;
            this.name3 = name3;
        }
    }

Dans ce cas, il semble être agrégé par le résultat du calcul de la clé. Ce serait étrange s'il y avait plusieurs clés avec la même clé, donc je suis convaincu.

Je l'ai essayé avec Integer type 2

Il semble qu'il soit agrégé par le résultat du calcul, mais juste au cas où, vérifiez si vous pouvez le voir sur le terrain.

    public static void main(String[] arg) {
        List<Integer> nums = List.of(1,2,3);

        List<Name> nameList = nums.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name(1, 1, 2));
        nameList.add(new Name(1, 2, 1));

        Map<Integer,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + it.getName3()));
        System.out.println(map);
        // => 2=[Main.Name(name1=1, name2=1, name3=1), Main.Name(name1=1, name2=2, name3=1)], 3=[Main.Name(name1=1, name2=1, name3=2)], 4=[Main.Name(name1=2, name2=2, name3=2)], 6=[Main.Name(name1=3, name2=3, name3=3)]}

    }

    @Data
    private static class Name {
        private Integer name1;
        private Integer name2;
        private Integer name3;

        public Name(Integer name1, Integer name2, Integer name3) {
            this.name1 = name1;
            this.name2 = name2;
            this.name3 = name3;
        }
    }

Il l'a également vu en type Integer.

Je l'ai essayé avec Integer type 3

Il s'agit du troisième essai de type Integer. Supposons maintenant que vous convertissiez la clé en chaîne et que vous l'utilisiez comme clé.

    public static void main(String[] arg) {
        List<Integer> nums = List.of(1,2,3);

        List<Name> nameList = nums.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name(1, 2, 1));
        nameList.add(new Name(2, 1, 1));

        Map<String,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2()));
        System.out.println(map);
        // => {1-1=[Main.Name(name1=1, name2=1, name3=1)], 2-1=[Main.Name(name1=2, name2=1, name3=1)], 1-2=[Main.Name(name1=1, name2=2, name3=1)], 2-2=[Main.Name(name1=2, name2=2, name3=2)], 3-3=[Main.Name(name1=3, name2=3, name3=3)]}
    }

    @Data
    private static class Name {
        private Integer name1;
        private Integer name2;
        private Integer name3;

        public Name(Integer name1, Integer name2, Integer name3) {
            this.name1 = name1;
            this.name2 = name2;
            this.name3 = name3;
        }
    }

Dans ce cas, la commande semble être prise en considération.

Essayez de spécifier 3 clés

Vérifions s'il n'y a pas de problème même si on augmente le nombre de clés.

    public static void main(String[] arg) {
        List<String> names = List.of("taro", "jiro", "saburo");

        List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
        nameList.add(new Name("taro","taro","jiro"));
        nameList.add(new Name("taro","jiro","taro"));

        Map<String,List<Name>> map = nameList.stream()
                .collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2() + "-" + it.getName3()));
        System.out.println(map);
        // => {jiro-jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], taro-jiro-taro=[Main.Name(name1=taro, name2=jiro, name3=taro)], taro-taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro)], taro-taro-jiro=[Main.Name(name1=taro, name2=taro, name3=jiro)], saburo-saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)]}

    }

    @Data
    private static class Name {
        private String name1;
        private String name2;
        private String name3;

        public Name(String name1, String name2, String name3) {
            this.name1 = name1;
            this.name2 = name2;
            this.name3 = name3;
        }
    }

Il semble que vous pouvez augmenter le nombre de clés.

Résumé

J'ai pensé essayer un peu plus, mais maintenant que j'ai saisi la tendance, je terminerai en résumant le fonctionnement de goingupingBy.

Fondamentalement, il semble que ce soit une méthode qui crée le champ spécifié dans la clé selon l'expression Lambda et l'agrège par cette valeur. S'il s'agit d'un type String, vous pouvez voir que l'ordre spécifié et le champ sont liés et que la clé l'est, et s'il s'agit d'un type numérique, vous pouvez voir si le résultat créé est le même car le résultat du calcul du champ spécifié est la clé. Je vais.

à la fin

Il s'avère que goupingBy est vraiment puissant. Cela peut être encore plus fort lorsqu'il est combiné avec d'autres méthodes dans Collectors, mais j'aimerais saisir une autre opportunité (je ne sais pas s'il y a une opportunité).

Au fait, je n'ai pas examiné le traitement interne de Java dans cet article, donc je ne sais pas pourquoi cela se produit!

Recommended Posts

L'histoire de Collectors.groupingBy que je veux garder pour la postérité
Liste de réglages de Glassfish que je souhaite conserver pour le moment
Je veux var_dump le contenu de l'intention
Je veux que vous utilisiez Enum # name () pour la clé de SharedPreference
Je veux connaître la réponse de l'application Janken
Je souhaite afficher le nom de l'affiche du commentaire
Je veux retourner la position de défilement de UITableView!
Je souhaite modifier le paramètre de sortie du journal de UtilLoggingJdbcLogger
Je souhaite renvoyer plusieurs valeurs de retour pour l'argument saisi
[Ruby] Je souhaite inverser l'ordre de la table de hachage
L'histoire que je voulais développer Zip
Je veux que vous racontiez que l'erreur a été résolue lorsque vous avez poignardé le chargeur dans le coin de la tête
Je veux comprendre le flux des paramètres de demande de traitement Spring
Je veux limiter l'entrée en réduisant la plage de nombres
Je veux contrôler le message d'erreur par défaut de Spring Boot
Je veux changer la valeur de l'attribut dans Selenium of Ruby
Je veux connaître le JSP du portlet ouvert lors du développement de Liferay
L'histoire du passage d'Amazon RDS pour MySQL à Amazon Aurora Serverless
[Ruby] Je souhaite extraire uniquement la valeur du hachage et uniquement la clé
L'histoire de la sortie de l'application Android sur le Play Store pour la première fois.
Je veux passer l'argument d'Annotation et l'argument de la méthode d'appel à aspect
Je veux obtenir le nom de champ du champ [Java]. (Vieux ton d'histoire)
Une histoire à laquelle j'étais accro à deux reprises avec le paramètre de démarrage automatique de Tomcat 8 sur CentOS 8
Je voulais écrire un processus équivalent à une instruction while avec l'API Java 8 Stream
L'histoire de l'introduction de la communication Ajax à Ruby
L'histoire de la montée de la série Spring Boot 1.5 à la série 2.1
L'histoire de l'ajout du dernier Node.js à DockerFile
Je veux tronquer après la virgule décimale
Je veux obtenir la valeur en Ruby
[Android] Je souhaite créer un ViewPager pouvant être utilisé pour les didacticiels
Je veux que vous utilisiez Scala comme meilleur Java pour le moment
Je veux voir le contenu de Request sans dire quatre ou cinq
Je veux obtenir récursivement la superclasse et l'interface d'une certaine classe
[Java] Je souhaite calculer la différence par rapport à la date
Je veux intégrer n'importe quel TraceId dans le journal
J'étais accro au record du modèle associé
Comprenez en lisant l'article! Résumé des contenus que les débutants d'Elasticsearch souhaitent supprimer
05. J'ai essayé de supprimer la source de Spring Boot
Je veux juger la gamme en utilisant le diplôme mensuel
J'ai essayé de réduire la capacité de Spring Boot
Je souhaite utiliser le mode sombre avec l'application SWT
[Active Admin] Je souhaite spécifier l'étendue de la collection à afficher dans select_box
[Rails] Je souhaite afficher la destination du lien de link_to dans un onglet séparé
Je veux appeler la méthode principale en utilisant la réflexion
7 choses que je veux que tu gardes pour que ça ne devienne pas un putain de code