2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Java StreamのcollectがSQLのgroupBy的なことができる件

Last updated at Posted at 2023-08-12

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}
2
2
0

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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?