LoginSignup
50
51

More than 5 years have passed since last update.

[Java 8][Stream API] オブジェクトの List から Map を生成する方法を模索する

Posted at

はじめに

Java 8 を導入し始めて既に半年以上になるけど、未だに Java 8 の Stream API で複雑な処理をしようとすると頭を抱えてしまうので、その時々の 自分なりの ベストプラクティスをメモしておくことにします。

今回のお題

Stream API に慣れてくると、データクラスのオブジェクトからなる List オブジェクトに対して、データクラスのある属性をグルーピングのキーに、もう一つ別の属性をグルーピングされた結果を保持する List の構成要素として持つような Map オブジェクトを Stream API で華麗に表現したくなるのが人間ってもんです。

今回はこの問題について、 Collectors.toMap() を使った方法と Collectors.groupingBy()Collectors.mapping() を組み合わせて使った方法で解消してみることにしました。

実装例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * オブジェクトのリストから、そのオブジェクトの属性を key / values にした Map を Java 8 の Stream で生成する。
 */
public class HowToBuildMapFromObjectList {
    public static void main(String[] args) {
        List<GroupUser> groupUsers = Arrays.asList(
                GroupUser.newGroupUser("でぃずにーぐみ", "泡姫(ありえる)"),
                GroupUser.newGroupUser("じぶりぐみ", "永瞳月(ととろ)"),
                GroupUser.newGroupUser("でぃずにーぐみ", "黄熊(ぷう)"),
                GroupUser.newGroupUser("じぶりぐみ", "今鹿(なうしか)")
        );

        // Collectors.toMap() を使う
        System.out.println(byCollectorsToMap(groupUsers));

        // Collectors.groupingBy() と Collectors.mapping() を組み合わせて使う
        System.out.println(byGroupingByAndMapping(groupUsers));
    }

    /**
     * {@link Collectors#toMap(Function, Function, BinaryOperator)} を使う方法。
     */
    static Map<String, List<String>> byCollectorsToMap(List<GroupUser> groupUsers) {
        return groupUsers.stream()
                .collect(Collectors.toMap(
                        GroupUser::groupName,
                        gu -> new ArrayList<>(Collections.singletonList(gu.userName())),
                        (l1, l2) -> Stream.concat(l1.stream(), l2.stream()).collect(Collectors.toList())));
    }

    /**
     * {@link Collectors#groupingBy(Function, Collector)} と
     * {@link Collectors#mapping(Function, Collector)} を組み合わせて使う方法。
     */
    static Map<String, List<String>> byGroupingByAndMapping(List<GroupUser> groupUsers) {
        return groupUsers.stream()
                .collect(Collectors.groupingBy(
                        GroupUser::groupName,
                        Collectors.mapping(
                                GroupUser::userName,
                                Collectors.toList())));
    }

    /**
     * グループ名とユーザ名を保持するよくあるクラスです。
     */
    static class GroupUser {
        private String groupName;
        private String userName;

        static GroupUser newGroupUser(String groupName, String userName) {
            GroupUser o = new GroupUser();
            o.groupName = groupName;
            o.userName = userName;
            return o;
        }

        public String groupName() {
            return groupName;
        }

        public String userName() {
            return userName;
        }
    }
}

感想

  • Collectors.groupingBy()Collectors.mapping() を組み合わせて使った実現方法が断然スマートに見えますね
  • これよりもっとスマートな解決方法はないものだろうか…
50
51
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
51