Collectors#filteringが欲しくなる件

以前,@cero_tさんが書いたブログで「部署をキーにしたMapを作ったうえで、Mapの値のほうには給与が1000を超える社員の人数を入れています。フィルタ処理を入れたいがために、ただの集計処理が使えず、Streamを利用しています。」ということがあって,それなりに綺麗に書ける方法を提案しました.

大本がこのコード.

Map<Dept, Long> groupByDeptAndFilter2(List<Emp> list) {
    return list.stream()
            .collect(Collectors.groupingBy(emp -> emp.dept))
            .entrySet()
            .stream()
            .collect(Collectors.toMap(entry -> entry.getKey(),
                    entry -> entry.getValue()
                            .stream()
                            .filter(emp -> emp.sal > 1000)
                            .count()));
}

で,提案したコードが以下.

Map<Dept, Long> groupByDeptAndFilter1(List<Emp> list) {
    return list.stream()
            .collect(Collectors.groupingBy(emp -> emp.dept, Collectors.collectingAndThen(Collectors.toList(),
                    emps -> emps.stream()
                            .filter(e -> e.sal > 1000)
                            .count())));
}

提案したコードのほうが短くなっていて,比較的シンプルなのだけど,どちらのコードも一回Listを作ってる辺りで,凄くダサいし,メモリとか性能的にもよくない.

そもそもフィルタリングしたいので,Collectors#mappingのようにfilteringしたCollectorを作ってくれる奴がいればもっとクールに書けるのではという話.

そこでfilteringを以下のように定義する.

    public static <T, A, R> Collector<T, ?, R> filtering(Predicate<? super T> predicate, Collector<? super T, A, R> collector) {
        BiConsumer<A, ? super T> downstream = collector.accumulator();
        return Collector.<T, A, R>of(collector.supplier(),
                (a, t) -> {if (predicate.test(t)) downstream.accept(a, t);},
                collector.combiner(), collector.finisher(),
                collector.characteristics().stream().toArray(Collector.Characteristics[]::new));
    }

条件にあった時にしかaccumulateしないようにしたCollectorを生成している.

今回の場合はこのfilteringをcountingを用いると以下のように書ける.

Map<Dept, Long> groupByDeptAndFilter(List<Emp> list) {
    return list.stream()
            .collect(groupingBy(emp -> emp.dept, filtering(emp -> emp.sal > 1000, counting())));
}

何気にこれが正解な気がする.
http://ideone.com/83Lzb7

問題はfilteringがCollectorsにないっていうこと.
誰かOpenJDKに提案して←
提案してみた
雰囲気良さそう



行けちゃう.
現状ではこれが一番よさ気

Map<Dept, Long> groupByDeptAndFilter(List<Emp> list) {
    return list.stream()
            .collect(toMap(e -> e.dept, e -> e.sal > 1000 ? 1L : 0L, Long::sum));
}

http://ideone.com/lY0Pkz

filteringを提案してしまったけど,どうしようw


filtering,マージされた