java8 中Collectors应用

我们可以看一个简单的例子

@Data
public class Person{
    private String firstName;
    private String lastName;
}

public class Test{

    /**
     * 按照FirstName分组统计,key=FirstName,value是同一FirstName的总人数
     *
     * @param persons persons
     */
    public static Map<String, Integer> stat(List<Person> persons) {
        Objects.requireNonNull(persons);
        Map<String, Integer> result = new HashMap<>();
        ConcurrentMap<String, List<Person>> collect = 
   persons.parallelStream()
          .collect(Collectors.groupingByConcurrent(Person::getFirstName));

        collect.forEach((key, value) -> result.put(key, value.size()));
        return result;
    }

}

我们看到这样的例子,它用了Collectors,但是我们又看到它似乎用得不够彻底,因为我们看到为了统计List<Person>的长度,用额外用了一个forEach,同时不得不多引入一个局部变量result,那看上去至少有3行代码,那有没有办法只用1行代码解决呢?毕竟在java8中经常可以用一行代码完成以前的很多行的代码的功能。

在我们再次看Collectors源码中有这么一个方法

    public static <T> Collector<T, ?, Long>
    counting() {
        return summingLong(e -> 1L);
    }

一看名字——counting(计数),好像符合我们的需求,其实用起来也挺好使的。我们可以把那3行代码写成下面一行代码

    public static Map<String, Integer> stat(List<Person> persons) {
        Objects.requireNonNull(persons);
        return persons.parallelStream()
.collect(Collectors.groupingByConcurrent(Person::getFirstName, Collectors.summingInt(i -> 1)));
    }

其实这是我们了解到groupingByConcurrent()还有一个重载版本,多了一个参数。

    public static <T, K, A, D>
    Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
                                                              Collector<? super T, A, D> downstream) {
        return groupingByConcurrent(classifier, ConcurrentHashMap::new, downstream);
    }

看到downstream我们就可以“用”counting()了,因为从上面的源码中我们看到counting()返回的是一个Collector。为什么是加了双引号的用呢?因为我们stat()的返回值是Map<String, Integer>,而counting()返回的是Long,直接调用会报编译错误。但是我们再看一眼,它实际是调用return summingLong(e -> 1L);所以我们第一时间想到应该还有一个方法summingInt(),再一看果然是有的。然后就是高光操作时刻, i -> 1, 这和counting()中的 e -> 1L “貌合神离”.

    public static <T> Collector<T, ?, Integer>
    summingInt(ToIntFunction<? super T> mapper) {
        return new CollectorImpl<>(
                () -> new int[1],
                (a, t) -> { a[0] += mapper.applyAsInt(t); },
                (a, b) -> { a[0] += b[0]; return a; },
                a -> a[0], CH_NOID);
    }