我们可以看一个简单的例子
@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); }