当サイトは、アフィリエイト広告を利用しています
Collectors.groupingByを使うと配列やListをグルーピングし、
Map<K,List< T >>のMap型データを取得できる
※Kは集約キー、Tは集約対象のオブジェクト。
似たようなことができる「partitioningby」メソッドもある
partitioningbyメソッドについては下記記事で紹介しています。
下記の本では streamAPI についても詳しく書いてありおススメです!
また当ブログで紹介しているjava8のstreamAPIを使った
コレクションや配列の操作方法を
下記記事でメソッド別にまとめています!
Collectors.groupingByは引数の数が1~3つの場合がある。
Map<K,List< T >> groupingBy(Function<T,K> classifier)
Map<K,D> groupingBy(Function<T,K> classifier,Collector<? super T,A,D> downstream)
ややこしいが
引数一つの場合は戻り値はMap<K,List< T >>固定だが
第二引数の関数で変換してやることで
など、集約キーで集約した結果の型を変えることができる。
M extends Map<K,D> groupingBy(Function<T,K> classifier, Supplier<M> mapFactory,Collector<? super T,A,D> downstream)
最終結果をtreeMapやLinkedHashMapで受け取りた時に使う
groupingByでは色々できることがあるのでサンプル実装していく
Personクラスを使う
public class Person {private final int id;private final String name;private final String address;Person(int id,String name, String address) {this.id = id;this.name = name;this.address = address;}public int getId() {return this.id;}public String getName() {return this.name;}public String getAddress() {return this.address;}@Overridepublic String toString(){return this.id + " " + this.name + " " + this.address;}}
groupingByに引数を一つだけ設定した場合
その引数を集約キーにしたMap<K,List< T >>で値を取得できる。
集約キーが一つのパターン
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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(Collectors.groupingBy(Person::getId //集約キー// t->t.getId() //メソッド参照を利用しないver));System.out.println(map);}}// 実行結果// {// 1=[// {1 TANAKA 東京},// {1 YOSHIDA 青森}// ],// 2=[// {2 YAMADA 山梨}// ],// 3=[// {3 OOTANI 熊本},// {3 TUJIMURA 京都},// {3 MURATA 仙台}// ],// 4=[// {4 OKAMOTO 大阪},// {4 MORITA 滋賀},// {4 KAWASAKI 岐阜}// ]// }
集約キーにしたidをキーにしてPersonオブジェクトがグルーピングされる
集約キーを複数にした場合
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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(Collectors.groupingBy(t->t.getId() + t.getAddress() //複合キーを作る));System.out.println(map);}}// 実行結果// {// 1東京=[// {1 TANAKA 東京}// ],// 2山梨=[// {2 YAMADA 山梨}// ],// 4滋賀=[// {4 MORITA 滋賀},// {4 KAWASAKI 滋賀}// ],// 4大阪=[// {4 OKAMOTO 大阪}// ],// 3熊本=[// {3 OOTANI 熊本},// {3 MURATA 熊本}// ],// 3京都=[// {3 TUJIMURA 京都}// ],// 1青森=[// {1 YOSHIDA 青森}// ]// }
複合キーにしたidとaddressをキーにしてPersonオブジェクトがグルーピングされる
引数を2つにして集約結果をList< T >以外の型で受け取る
Map<K,List< T >>のList< T >の部分をMapで受け取る
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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で取得するMap<Integer, Map<Integer,String>> map =personlist.stream().collect(Collectors.groupingBy(Person::getId,Collectors.toMap( //集約結果のMapを作成e->e.getId(), // キーはIde->e.getName(), // バリューはname(oldVal,newVal) -> newVal) // 後勝ち));System.out.println(map);}}// 実行結果// {// 1={1=YOSHIDA},// 2={2=YAMADA},// 3={3=MURATA},// 4={4=KAWASAKI}// }
集約結果がMapで取得できている。
Mapにキーが重複しているため後勝ちになっている
Map<K,List< T >>のList< T >の部分をSetで受け取る
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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","滋賀"));// グルーピング// 集約結果をSetで取得するMap<Integer, Set<Person>> map =personlist.stream().collect(Collectors.groupingBy(Person::getId,Collectors.toSet() //Setにする));System.out.println(map);}}// 実行結果// {// 1=[// {1 TANAKA 東京}, {1 YOSHIDA 青森}// ],// 2=[// {2 YAMADA 山梨}// ],// 3=[// {3 TUJIMURA 京都}, {3 MURATA 熊本}, {3 OOTANI 熊本}// ],// 4=[// {4 MORITA 滋賀}, {4 KAWASAKI 滋賀}, {4 OKAMOTO 大阪}// ]// }
Setで取得できる
Map<K,List< T >>のList< T >の部分を任意のListで受け取る
このサンプルではPerson.addressのListで受け取るようにする。
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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","滋賀"));// グルーピング// 集約結果をPerson.addressのListで取得Map<Integer, List<String>> nameByPerson= personlist.stream().collect(Collectors.groupingBy(Person::getId,Collectors.mapping( //集約結果をさらにmappingするPerson::getAddress, //集約キーごとにaddressをまとめるCollectors.toList() //まとめたaddressをListにする)));System.out.println(nameByPerson);}}// 実行結果// {// 1=[// 東京, 青森// ],// 2=[// 山梨// ],// 3=[// 熊本, 京都, 熊本// ],// 4=[// 大阪, 滋賀, 滋賀// ]// }
集約キーごとにadrressのListが取得できる。
groupingByに引数を3つ設定して
その引数を集約キーにしたMap<K,List< T >>のMapを
任意のMapクラスで取得する
Collectors.mappingを使って戻り値のMapを
LinkedHashMapにしてみる
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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.groupingBy(Person::getId,LinkedHashMap::new, //LinkedHashMapで取得Collectors.mapping(p->p,Collectors.toList())) //mappingを使う);System.out.println(nameByPerson);System.out.println(nameByPerson instanceof LinkedHashMap);}}// 実行結果// {// 1=[1 TANAKA 東京, 1 YOSHIDA 青森],// 2=[2 YAMADA 山梨],// 3=[3 OOTANI 熊本, 3 TUJIMURA 京都, 3 MURATA 熊本],// 4=[4 OKAMOTO 大阪, 4 MORITA 滋賀, 4 KAWASAKI 滋賀]// }// true
mappingを使って任意のListにすることもできる
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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","滋賀"));// グルーピング// 集約結果をPerson.addressのListで取得LinkedHashMap<Integer, List<String>> nameByPerson= personlist.stream().collect(Collectors.groupingBy(Person::getId,LinkedHashMap::new, //LinkedHashMapで取得Collectors.mapping(Person::getAddress,Collectors.toList())));System.out.println(nameByPerson);}}// 実行結果// {// 1=[// 東京, 青森// ],// 2=[// 山梨// ],// 3=[// 京都, 熊本, 熊本// ],// 4=[// 滋賀, 滋賀, 大阪// ]// }
他にもgroupingBy後に編集できるCollectorsのサンプルをのせる
id毎にグルーピングしてからidごとのidの合計を出す
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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","滋賀"));// グルーピング// 集約結果をPerson.addressのListで取得Map<Integer, Integer> nameByPerson= personlist.stream().collect(Collectors.groupingBy(Person::getId,Collectors.reducing(0,//初期値(value)->value.getId(),//mappingする(accum, value)-> accum + value //計算)));System.out.println(nameByPerson);}}// 実行結果// {1=2, 2=2, 3=9, 4=12}
mappingのところでMap< Integer, Integer >のInteger型に変換してやる必要がある
ageを追加する
public class Person {private final int id;private final String name;private final String address;Person(int id,String name, String address) {this.id = id;this.name = name;this.address = address;}public int getId() {return this.id;}public String getName() {return this.name;}public String getAddress() {return this.address;}@Overridepublic String toString(){return this.id + " " + this.name + " " + this.address;}}
id毎にグルーピングしてからidごとのid内で最大のageのオブジェクトを取得する
import java.util.*;import java.util.stream.Collectors;public class Main {public static void main(String[] args) throws Exception {// PersonのListを作成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));// グルーピング// 集約結果をPerson.addressのListで取得Map<Integer, Optional<Person>> nameByPerson= personlist.stream().collect(Collectors.groupingBy(Person::getId,//idで集約Collectors.maxBy(Comparator.comparing(Person::getAge))//各グループでageが最大値を取得));System.out.println(nameByPerson);}}// 実行結果// {// 1=Optional[{1 YOSHIDA 青森21}],// 2=Optional[{2 YAMADA 山梨23}],// 3=Optional[{3 TUJIMURA 京都80}],// 4=Optional[{4 KAWASAKI 滋賀78}]// }
処理の流れイメージとしては
のような感じだと思う。
一旦はMap<K,List< T >>の形になると考えれば処理がイメージしやすい。
下記を参考させていただきました
-【Stream API】groupingBy と mapping でリストをマッピングしつつグルーピングする
下記の本ではstreamAPIについても詳しく書いてあり、おススメです!