基本構文
Collectors.groupingByは引数の数が1~3つの場合がある。
引数が1つの場合(デフォルト)
Map<K,List< T >> groupingBy(Function<T,K> classifier)
-
第一引数は集約キーを返す関数を関数型インターフェースのFunction型
-
Tはオブジェクト。
-
Kは集約キー。基本的にはTの属性のどれかになる。
引数が2つの場合
Map<K,D> groupingBy(Function<T,K> classifier,Collector<? super T,A,D> downstream)
-
第一引数は集約キーを返す関数を関数型インターフェースのFunction型
-
Kは集約キー。基本的にはTの属性のどれかになる。
-
第二引数は集約キーで集約した結果を指定の型に変換する関数等をセットする
-
Dは第二引数で変換した結果を同じ型になる
ややこしいが
引数一つの場合は戻り値は
Map<K,List< T >>
固定だが
第二引数の関数で変換してやることで
-
Map<K,Set< T >>
-
Map<K,Map< K,V >>
など、集約キーで集約した結果の型を変えることができる。
引数が3つの場合
M extends Map<K,D> groupingBy(Function<T,K> classifier, Supplier<M> mapFactory,Collector<? super T,A,D> downstream)
-
第一引数は集約キーを返す関数を関数型インターフェースのFunction型
-
Kは集約キー。基本的にはTの属性のどれかになる。
-
第二引数は最終結果として取得したいMapの実装クラス(treeMapやLinkedHashMap)を指定する
-
第三引数は集約キーで集約した結果を指定の型に変換する関数等をセットする
-
Dは第二引数で変換した結果を同じ型になる
最終結果をtreeMapやLinkedHashMapで受け取りた時に使う
groupingByを使ってサンプル実装してみる
groupingByでは色々できることがあるのでサンプル実装していく
集約対象クラス
Personクラスを使う
private final String name;
private final String address;
Person(int id,String name, String address) {
public String getName() {
public String getAddress() {
return this.id + " " + this.name + " " + this.address;
デフォルト
groupingByに引数を一つだけ設定した場合
その引数を集約キーにした
Map<K,List< T >>
で値を取得できる。
単一項目のグルーピング
集約キーが一つのパターン
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","仙台"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","岐阜"));
Map<Integer, List<Person>> map =
personlist.stream().collect(
集約キーにしたidをキーにしてPersonオブジェクトがグルーピングされる
複数項目のグルーピング
集約キーを複数にした場合
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
Map<String, List<Person>> map =
personlist.stream().collect(
t->t.getId() + t.getAddress()
複合キーにしたidとaddressをキーにしてPersonオブジェクトがグルーピングされる
集約結果の型を変えて受け取る
引数を2つにして集約結果をList< T >以外の型で受け取る
toMap
Map<K,List< T >>のList< T >の部分をMapで受け取る
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
Map<Integer, Map<Integer,String>> map =
personlist.stream().collect(
(oldVal,newVal) -> newVal)
集約結果がMapで取得できている。
Mapにキーが重複しているため後勝ちになっている
toSet
Map<K,List< T >>のList< T >の部分をSetで受け取る
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
Map<Integer, Set<Person>> map =
personlist.stream().collect(
Setで取得できる
mapping
Map<K,List< T >>のList< T >の部分を任意のListで受け取る
このサンプルではPerson.addressのListで受け取るようにする。
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
Map<Integer, List<String>> nameByPerson
= personlist.stream().collect(
System.out.println(nameByPerson);
集約キーごとにadrressのListが取得できる。
Mapクラスを指定して受け取る
groupingByに引数を3つ設定して
その引数を集約キーにした
Map<K,List< T >>
のMapを
任意のMapクラスで取得する
Collectors.mappingを使う
Collectors.mappingを使って戻り値のMapを
LinkedHashMapにしてみる
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
LinkedHashMap<Integer, List<Person>> nameByPerson
= personlist.stream().collect(
Collectors.mapping(p->p,Collectors.toList()))
System.out.println(nameByPerson);
System.out.println(nameByPerson instanceof LinkedHashMap);
任意のListにする
mappingを使って任意のListにすることもできる
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
LinkedHashMap<Integer, List<String>> nameByPerson
= personlist.stream().collect(
System.out.println(nameByPerson);
集約いろいろ
他にもgroupingBy後に編集できるCollectorsのサンプルをのせる
グループごとに合計する
id毎にグルーピングしてからidごとのidの合計を出す
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京"),
new Person(1,"YOSHIDA","青森"),
new Person(2,"YAMADA","山梨"),
new Person(3,"OOTANI","熊本"),
new Person(3,"TUJIMURA","京都"),
new Person(3,"MURATA","熊本"),
new Person(4,"OKAMOTO","大阪"),
new Person(4,"MORITA","滋賀"),
new Person(4,"KAWASAKI","滋賀"));
Map<Integer, Integer> nameByPerson
(accum, value)-> accum + value
System.out.println(nameByPerson);
mappingのところでMap< Integer,
Integer
>のInteger型に変換してやる必要がある
グループごとに最大値を出す
Personクラス
ageを追加する
private final String name;
private final String address;
Person(int id,String name, String address) {
public String getName() {
public String getAddress() {
return this.id + " " + this.name + " " + this.address;
id毎にグルーピングしてからidごとのid内で最大のageのオブジェクトを取得する
import java.util.stream.Collectors;
public static void main(String[] args) throws Exception {
List<Person> personlist = Arrays.asList(
new Person(1,"TANAKA","東京",11),
new Person(1,"YOSHIDA","青森",21),
new Person(2,"YAMADA","山梨",23),
new Person(3,"OOTANI","熊本",45),
new Person(3,"TUJIMURA","京都",80),
new Person(3,"MURATA","熊本",22),
new Person(4,"OKAMOTO","大阪",2),
new Person(4,"MORITA","滋賀",54),
new Person(4,"KAWASAKI","滋賀",78));
Map<Integer, Optional<Person>> nameByPerson
Collectors.maxBy(Comparator.comparing(Person::getAge))
System.out.println(nameByPerson);
まとめ
処理の流れイメージとしては
-
grupingByで集約キーごとのListを作成する。
-
Map<K,List< T >>
の形で結果が生成される(デフォルトの形)
-
Map< K,
List < T >
>の部分を色々編集したり型変えたりして返却する。
のような感じだと思う。
一旦は
Map<K,List< T >>
の形になると考えれば処理がイメージしやすい。
参考
下記を参考させていただきました
-
【Stream API】groupingBy と mapping でリストをマッピングしつつグルーピングする
参考書籍
下記の本ではstreamAPIについても詳しく書いてあり、おススメです!