collect() と Collectors

終端操作の一つであるcollect()メソッドを使用した処理。
ストリームの要素をまとめて一つにする際に使用する。
collect()メソッドにはCollectorを引数に持たせるが、
汎用的なCollectorはCollectorsクラスによって提供されている。

以下、Collectorsのメソッドをいろいろ試す。

toList()でStreamのリスト化

Stream<String> stream = Stream.of("A", "B", "C", "D");
List<String> list = stream.collect(Collectors.toList());

joining()で文字列結合

Stream<String> stream = Stream.of("A", "B", "C", "D");
//引数なし(ただの連結)→ABCD
String joined = stream.collect(Collectors.joining());
//引数一つ(区切り文字)→A,B,C,D
String joinedWithComma = stream.collect(Collectors.joining(","));
//引数3つ(区切り文字とプレフィックス・サフィックス)→ [A,B,C,D]
String string = stream.collect(Collectors.joining(",", "[", "]"));

summingInt()で合計値の算出

Stream<String> stream2 = Stream.of("one", "two", "three", "four");
//summingInt()の引数はToIntFunctionのため、文字列をintに変換するラムダ式を渡している
//この例だと文字列長への変換
int sum = stream2.collect(Collectors.summingInt(s -> s.length()));
//15になる

averagingInt()で平均値の算出

Stream<String> stream2 = Stream.of("one", "two", "three", "four");
//summingInt()と同様にマッピングラムダ式を渡す。
//戻り値はdouble型となる
double sum = stream2.collect(Collectors.averagingInt(s -> s.length()));
System.out.println(sum);
//3.75

toSet()でSetへ変換

Stream<String> stream = Stream.of("A", "B", "C", "D", "C", "A");
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);
//setに変換しているので重複を除いた要素A、B、C、Dとなる

toMap()でMapへ変換

Stream<String> stream = Stream.of("A", "BB", "CCC", "DDDD");
//第1引数に要素からキーを生成するFunction、
//第2引数にMapのValueにあたるオブジェクトを生成するFunction
Map<Integer, String> map = stream.collect(Collectors.toMap(s -> s.length(), s -> s));
//結果 → {1=A, 2=BB, 3=CCC, 4=DDDD}

//ただし、重複するキーがあると例外が発生する
//下の例だと文字列長が4桁のものが2つあるため、キー重複で例外発生
Stream<String> stream = Stream.of("A", "BB", "CCC", "DDDD", "EEEE");
Map<Integer, String> map = stream.collect(Collectors.toMap(s -> s.length(), s -> s));
//Exception in thread "main" java.lang.IllegalStateException: Duplicate key DDDD が発生!

//重複キーがありうる場合はオーバーロードされているtoMap()を使用する
//3つ目の引数で、重複した場合の関数を指定している。この例だと重複した場合に最初にMapに格納されていたほうを残す。
Map<Integer, String> map = stream.collect(Collectors.toMap(s -> s.length(), s -> s, (s1, s2) -> s1));

groupingBy()で要素をグループ化する

//下の例では、文字列長ごとにグループ化し、キーに文字列長、値にキーに属する要素のリストとなる。
List<String> list = Arrays.asList("java", "scala", "ruby", "C", "haskell", "Go");
Map<Integer, List<String>> map = list.stream().collect(Collectors.groupingBy(s -> s.length()));
System.out.println(map);

partitioningBy()で要素をtrueとfalseでグループ化する

Stream<String> stream = Stream.of("one", "two", "three", "four", "five", "six");
//partitioningBy()を使用して条件に合致するか否かでグループ化する。
//条件に合致したものとしないものをそれぞれリストとして保持したMapを生成する
Map<Boolean, List<String>> map = stream.collect(Collectors.partitioningBy(s -> s.length() < 4));
System.out.println(map);
//結果は→ {false=[three, four, five], true=[one, two, six]} 

mapping()によるマッピングおよびCollectorによる処理

//要素に対するマッピング処理を行った後、さらにCollectorによるリダクションを行う
List<String> list = Arrays.asList("java", "scala", "ruby", "C", "haskell", "Go");
Integer collect = list.stream().collect(Collectors.mapping(s -> s.length(), Collectors.summingInt(i -> i)));
//23

maxBy()による最大要素の生成

//maxBy()に渡すComparetorの比較にしたがって、最大要素を返却する。
//この例だと、辞書順の最大がOptional型で返却される
List<String> list = Arrays.asList("java", "scala", "ruby", "C", "haskell", "Go");
Optional<String> result = list.stream().collect(Collectors.maxBy(String::compareTo));
//値はOptionalでラップされて返されるので、get()を使用して値を取得する
System.out.println(result.get());

minBy()による最小要素の生成

//使い方は上述のmaxBy()とほぼ同じ。
//こちらは最小要素を返却する
List<String> list = Arrays.asList("java", "scala", "ruby", "C", "haskell", "Go");
Optional<String> result = list.stream().collect(Collectors.minBy(String::compareTo));
System.out.println(result.get());

他にもいろいろあるが、とりあえずこのあたりで・・。
ちなみに、Collectorsクラスはあくまでよく使うであろうリダクション処理を実装しているだけなので、 独自でCollectorインターフェースを実装したクラスを作成すればオリジナルのリダクション処理用クラスが作れる。
そっちはまた今度やる。