はじめに
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()
を組み合わせて使った実現方法が断然スマートに見えますね - これよりもっとスマートな解決方法はないものだろうか…