collect : グルーピング
Java8で追加されたStreamのcollectがSQLのgroupBy的なことが出来ることをしってびっくりした件
サンプルで使用するクラス
class MyClass {
private String param1;
private int param2;
public MyClass(String param1, int param2) {
this.param1 = param1;
this.param2 = param2;
}
public String getParam1() {
return param1;
}
public int getParam2() {
return param2;
}
}
class Prefecture {
private int id;
private String name;
private List<City> cities;
public Prefecture(int id, String name, List<City> cities) {
this.id = id;
this.name = name;
this.cities = cities;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public List<City> getCities() {
return cities;
}
}
class City {
private int id;
private String name;
private int population;
public City(CityEntity cityEntity) {
this.id = cityEntity.getId();
this.name = cityEntity.getName();
this.population = cityEntity.getPopulation();
}
public City(int id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getPopulation() {
return population;
}
@Override
public String toString() {
return "id:" + id + ", name:" + name + ", population:" + population;
}
}
グルーピング
param1の値で要素をグルーピング
List<MyClass> array = new ArrayList<MyClass>();
array.add(new MyClass("A", 1));
array.add(new MyClass("A", 2));
array.add(new MyClass("A", 3));
array.add(new MyClass("B", 4));
array.add(new MyClass("B", 5));
array.add(new MyClass("C", 6));
// param1の値でグルーピング
Map<String, List<MyClass>> elementByPrama1 = array.stream()
.collect(Collectors.groupingBy(MyClass::getParam1));
for(Map.Entry<String, List<MyClass>> element : elementByPrama1.entrySet()) {
System.out.println("param1: " + element.getKey());
// グリープされた子リスト出力
for (MyClass childElement : element.getValue()) {
System.out.println(" " + "param2: " + childElement.getParam2());
}
}
param1: A
param2: 1
param2: 2
param2: 3
param1: B
param2: 4
param2: 5
param1: C
param2: 6
グループごとに集計
param1毎に要素の数集計
List<MyClass> array = new ArrayList<MyClass>();
array.add(new MyClass("A", 1));
array.add(new MyClass("A", 2));
array.add(new MyClass("A", 3));
array.add(new MyClass("B", 4));
array.add(new MyClass("B", 5));
array.add(new MyClass("C", 6));
// param1毎に要素の数を集計
Map<String, Long> elementByPrama1 = array.stream()
.collect(Collectors.groupingBy(MyClass::getParam1,Collectors.counting()));
for(Map.Entry<String, Long> element : elementByPrama1.entrySet()) {
System.out.println("param1: " + element.getKey() + " ,count:" + element.getValue());
}
param1: A ,count:3
param1: B ,count:2
param1: C ,count:1
param1毎にparam2の値を集計
List<MyClass> array = new ArrayList<MyClass>();
array.add(new MyClass("A", 1));
array.add(new MyClass("A", 2));
array.add(new MyClass("A", 3));
array.add(new MyClass("B", 4));
array.add(new MyClass("B", 5));
array.add(new MyClass("C", 6));
// param1毎にparam2の値を集計
Map<String, Integer> elementByPrama1 = array.stream()
.collect(Collectors.groupingBy(MyClass::getParam1,Collectors.summingInt(MyClass::getParam2)));
for(Map.Entry<String, Integer> element : elementByPrama1.entrySet()) {
System.out.println("param1: " + element.getKey() + " ,totalParam2:" + element.getValue());
}
param1: A ,totalParam2:6
param1: B ,totalParam2:9
param1: C ,totalParam2:6
注意
Mapを宣言する時、合計値の型はintとかのプリミティブ型ではなく、Integerなどのオブジェクト型を使用すること。理由は「Collectors.summingInt」の関数の定義がそうだから!
×
Map<String, int> elementByPrama1 = ...
〇
Map<String, Integer> elementByPrama1 = ...
グループごとに集計(集計処理カスタマイズ)
マイナス値は集計しない。
List<MyClass> array = new ArrayList<MyClass>();
array.add(new MyClass("A", -1));
array.add(new MyClass("A", 2));
array.add(new MyClass("A", 3));
array.add(new MyClass("B", -4));
array.add(new MyClass("B", 5));
array.add(new MyClass("C", 6));
// param1毎にparam2の値を集計
Map<String, Integer> elementByPrama1 = array.stream()
.collect(Collectors.groupingBy(MyClass::getParam1,Collectors.summingInt(e -> e.getParam2() > 0 ? e.getParam2() : 0)));
for(Map.Entry<String, Integer> element : elementByPrama1.entrySet()) {
System.out.println("param1: " + element.getKey() + " ,totalParam2:" + element.getValue());
}
param1: A ,totalParam2:5
param1: B ,totalParam2:5
param1: C ,totalParam2:6
ネストしている配列の集計
[
{東京, [{新宿区, 100}, {渋谷区, 200}]},
{神奈川, [{横浜, 300}, {川崎, 400}]},
]
を
[
{東京, 300},
{神奈川, 700},
]
にするイメージ。
List<Prefecture> prefectures = new ArrayList<Prefecture>();
prefectures.add(new Prefecture(1, "東京", Arrays.asList(
new City(11, "新宿区", 100),
new City(12, "渋谷区", 200))
));
prefectures.add(new Prefecture(2, "神奈川", Arrays.asList(
new City(21, "横浜", 300),
new City(22, "川崎", 400))
));
// 都道府県IDごとに人口取得
Map<Integer, Integer> populationByPrefecure = prefectures.stream()
.collect(Collectors.groupingBy(Prefecture::getId,
Collectors.summingInt(p -> p.getCities().stream().collect(Collectors.summingInt(City::getPopulation))
)));
// populationByPrefecure => {1=300, 2=700}