Java8 の Stream API は Stream#collect
メソッドを使って様々な集約を行う事ができます。
java.util.stream.Collectors
で様々な Collector が提供されていますが、Collectors.collectingAndThen
のような合成が中心で同時に二つの集約を行うといった事が簡単にできません。
そのため、ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API の「7. streamの外に結果を残す(禁止度:A)」で書かれている様な思わず禁止したくなっちゃう様なコードを書かざるを得ません。
// 引用: ラムダ禁止について本気出して考えてみた
// 7. streamの外に結果を残す(禁止度:A)
void averageAndSum1(List<Emp> list) {
final Map<String, Integer> dummy = new HashMap<>();
dummy.put("RESULT", 0);
double ave = list.stream()
.mapToInt(emp -> {
int sal = emp.getSal();
dummy.put("RESULT", dummy.get("RESULT") + sal);
return sal;
})
.average()
.getAsDouble();
System.out.println("ave=" + ave + ",sum=" + dummy.get("RESULT"));
}
そこで、複数の Collector を合成して一つの Collector にする product Collector を作成しました。
これを使うと以下の様に、複数の集約したい場合に上記のようなややこしい事をせず、直感的に記述する事が可能になります。 (上記の例はSummaryStatistics使うと簡単に書き変えられるのでちょっとお題を変えてます)
public void richmanByDeptAndTotalSummaryOfSalary(final List<Emp> list) {
final T2<Map<Department, Optional<Emp>>, IntSummaryStatistics> result = list.stream()
.collect(product(
groupingBy(Emp::getDepartment, maxBy(comparingInt(Emp::getSalary))), // 部署毎で一番給与の多い従業員
summarizingInt(Emp::getSalary) // 全体の給与のサマリ(平均とか最大値・最小値など)
));
System.out.println("mostRichmanByDept=" + result._1 + ", salarySummary=" + result._2);
}
これならラムダ禁止とか言われなくて済みますね!
MIT License にしとくので適当にコピペして使ってください。