Help us understand the problem. What is going on with this article?

「Dictの値をキーにキーを集計し、集計されたキーに対して任意の関数を実行したDictを返す」をJava8でやってみたら「[Java 8][Stream API] オブジェクトの List から Map を生成する方法を模索する」がすっきり書けた

More than 5 years have passed since last update.

@keithseahus さんの Dictの値をキーにキーを集計し、集計されたキーに対して任意の関数を実行したDictを返す というElixir に関する投稿を読んだ。
Elixirの事は全く知らないけど、これJava8のStreamAPIだとどうなるんだろうと思ったのでやってみた。

やってること

Dict(JavaでいうとMap)のキーと値を入れ替えたDictの作成。
HadoopでいうとInverseMapper

元投稿によるとElixirではEnum.group_by/3というのがあるらしい。
これはJava8のCollectors.groupingBy(Function<? super T,? extends K> classifier)に相当するもので、これだと値をマップ(変換)する手段がないのでgroup_byを拡張したということらしい。

Java8でやってみた

Java8のCollectorsにはgroupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstreamというのもあって、値のマップもできるのでこれを使ってみた。

    public static void main(String[] args) {

        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 1);
        map.put("b", 0);
        map.put("c", 4);
        map.put("d", 0);
        map.put("e", 1);
        map.put("f", 1);

        Map<Integer, List<String>> result = map.entrySet()
                .stream()
                .collect(Collectors.groupingBy(
                        Map.Entry::getValue,
                        Collectors.mapping(Map.Entry::getKey, Collectors.toList())
                ));

        System.out.println(result);
    }

元投稿に合わせてメソッドにしてみる。

    public static void main(String[] args) {

        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 1);
        map.put("b", 0);
        map.put("c", 4);
        map.put("d", 0);
        map.put("e", 1);
        map.put("f", 1);

        Map<Integer, List<String>> result = map.entrySet()
                .stream()
                .collect(groupingBy(Map.Entry::getValue, Map.Entry::getKey));

        System.out.println(result); // => {0=[b, d], 1=[a, e, f], 4=[c]}
    }

    private static <T, K, V> Collector<T, ?, Map<K, List<V>>> groupingBy(
            Function<T, K> keyMapper,
            Function<T, V> valueMapper
    ) {
        return Collectors.groupingBy(
                keyMapper,
                Collectors.mapping(valueMapper, Collectors.toList())
        );
    }

どこかで見た気が...

とここまで書いて少し前に見た @komiya_atsushi さんの [Java 8][Stream API] オブジェクトの List から Map を生成する方法を模索する という投稿にあったコードに似ている気がしたので、作成したgroupingByメソッドをそちらのコードでも使ってみた。

元のコード
    static Map<String, List<String>> byGroupingByAndMapping(List<GroupUser> groupUsers) {
        return groupUsers.stream()
                .collect(Collectors.groupingBy(
                        GroupUser::groupName,
                        Collectors.mapping(
                                GroupUser::userName,
                                Collectors.toList())));
    }
今回作成したgroupingBy()メソッドを使用
    static Map<String, List<String>> byGroupingByAndMapping(List<GroupUser> groupUsers) {
        return groupUsers.stream()
                .collect(groupingBy(GroupUser::groupName, GroupUser::userName));
    }

単にショートカット的なメソッドを作って使っただけなんだけど、少しスッキリしたような気がする。
頻繁に使うようであればこういうのをユーティリティクラスに用意しておいたほうがいいかもしれない。

tag1216
Qiita戦闘力はキュイレベルです! /作ったもの ◆QiiTrend:https://qiitrend.herokuapp.com/ ◆Qiiner:https://qiiner.tag1216.net/ ◆Qiitaでいいねしたら草生えるページ:https://qiiner.tag1216.net/likes-heatmap
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away