当サイトは、アフィリエイト広告を利用しています

【java8】streamのpartitioningbyの使い方

作成日:2022月04月10日
更新日:2023年12月14日

StreamAPIのpartitioningByとは?

Collectors.groupingByを使うと指定した条件にしたがって配列やListを
2つに分割してMap<boolean,List< T >>のMap型データを取得できる
※Tは分割対象のオブジェクト。

似たようなことができる「groupingby」メソッドもある。
groupingbyメソッドはpartitioningByメソッドの上位互換的なもので
Listを2分割ではなく、groupに分割できる
groupingbyメソッドについては下記記事で紹介しています。

下記の本では streamAPI についても詳しく書いてありおススメです!

また当ブログで紹介しているjava8のstreamAPIを使った
コレクションや配列の操作方法を
下記記事でメソッド別にまとめています!

基本構文

Collectors.partitioningByは引数の数が1~2つの場合がある。

引数が1つの場合(デフォルト)

java
Map<boolean,List< T >> partitioningBy(Predicate<? super T> predicate)
  • 第一引数は分割条件を与えてboolで値を返す関数を関数型インターフェースのpredicate型
  • Tはオブジェクト。

引数が2つの場合

java
Map<K,D> partitioningBy(Predicate<? super T> predicate,Collector<? super T,A,D> downstream)
  • 第一引数は分割条件を与えてboolで値を返す関数を関数型インターフェースのpredicate型
  • 第二引数は分割条件で分割した結果を指定の型に変換する関数等をセットする
  • Dは第二引数で変換した結果を同じ型になる

引数一つの場合は戻り値はMap<boolean,List< T >>固定だが
第二引数の関数で変換してやることで

  • Map<boolean,Map< K,V >>
  • Map<boolean,Set< V >>

など、分割した結果の型を変えることができる。

partitioningByを使ってサンプル実装してみる

集約対象クラス

Personクラスを使う

Person.java
public class Person {
private final int id;
private final String name;
private final String address;
private final int age;
Person(int id,String name, String address,int age) {
this.id = id;
this.name = name;
this.address = address;
this.age = age;
}
public int getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getAddress() {
return this.address;
}
public int getAge() {
return this.age;
}
@Override
public String toString()
{
return "{" + this.id + " " + this.name + " " + this.address + this.age +"}";
}
}

引数が1つの場合

partitioningByに引数を一つだけ設定した場合
その引数を分割関数で分割したMap<boolean,List< T >>で値を取得できる。

Main.java
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));
// 分割
Map<Boolean, List<Person>> map =
personlist.stream().collect(
Collectors.partitioningBy(
person -> person.getId()>2 //分割条件
)
);
System.out.println(map);
}
}
// 実行結果
// {
// false=[
// {1 TANAKA 東京11},
// {1 YOSHIDA 青森21},
// {2 YAMADA 山梨23}
// ],
// true=[
// {3 OOTANI 熊本45},
// {3 TUJIMURA 京都80},
// {3 MURATA 熊本22},
// {4 OKAMOTO 大阪2},
// {4 MORITA 滋賀54},
// {4 KAWASAKI 滋賀78}
// ]
// }

指定した分割条件で分割されている

分割結果の型を変えて受け取る

引数を2つにして分割結果をList< T >以外の型で受け取る

toMap

Map<boolean,List< T >>のList< T >の部分をMapで受け取る

Main.java
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));
// 分割
// 結果をMap<Integer,String>で取得する
Map<Boolean, Map<Integer,String>> map =
personlist.stream().collect(
Collectors.partitioningBy(
person -> person.getId()>2, //分割条件
Collectors.toMap( //集約結果のMapを作成
e->e.getId(), // キーはId
e->e.getName(), // バリューはname
(oldVal,newVal) -> newVal) // 後勝ち
)
);
System.out.println(map);
}
}
// 実行結果
// {false={1=YOSHIDA, 2=YAMADA}, true={3=MURATA, 4=KAWASAKI}}

分割結果がMapで取得できている。
Mapにキーが重複しているため後勝ちになっている

mapping

Map<K,List< T >>のList< T >の部分を任意のListで受け取る
このサンプルではPerson.ageのListで受け取るようにする。

Main.java
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));
// 分割
// 結果をMap<Integer,String>で取得する
Map<Boolean, List<Integer>> map =
personlist.stream().collect(
Collectors.partitioningBy(
person -> person.getId()>2, //分割条件
Collectors.mapping( //分割結果をmappingする
Person::getAge, // ageをまとめてListにする
Collectors.toList()
)
)
);
System.out.println(map);
}
}
// 実行結果
// {false=[11, 21, 23], true=[45, 80, 22, 2, 54, 78]}

分割条件キーごとにageのListが取得できる。

分割後の編集

他にもpartitioningBy後に編集できるCollectorsのサンプルをのせる

分割ごとに合計する

分割条件で分割してからに分割ごとのageの合計を出す

Main.java
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));
// 分割
// 分割後にageを合計する
Map<Boolean, Integer> map =
personlist.stream().collect(
Collectors.partitioningBy(
person -> person.getId()>2, //分割条件
Collectors.reducing(
0,//初期値
Person::getAge, // 集計する対象
(accum, value)-> accum + value //集計関数
)
)
);
System.out.println(map);
}
}
// 実行結果
// {false=55, true=281}

分割ごとにageが合計されている。

分割ごとに最大値を出す

分割条件で分割後してから分割ごとに最小のageを返却する

Main.java
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));
// 分割
// 分割後にageを合計する
Map<Boolean, Optional<Person>> map =
personlist.stream().collect(
Collectors.partitioningBy(
person -> person.getId()>2, //分割条件
Collectors.minBy(
Comparator.comparing(Person::getAge)
)
)
);
System.out.println(map);
}
}
// 実行結果
// {false=Optional[{1 TANAKA 東京11}], true=Optional[{4 OKAMOTO 大阪2}]}
  • mimByは戻り値はOptionalになる

まとめ

基本的にgroupingByとほぼ一緒だった。
恐れらくpartitioningByでできることはgroupingByでもできると思われる。
※使いどころを考える必要がありそう

参考

クラスCollectors

参考書籍

下記の本ではstreamAPIについても詳しく書いてありおススメです!

新着記事

タグ別一覧
top