一、Collectors
java.util.stream.Collectors,是从JDK1.8开始新引入的一个类。从源码的类注释上,我们可以知道:Collectors实现了各种有用归约的操作,例如类型归类到新集合、根据不同标准汇总元素等。透过示例,能让我们眼前一亮,短短的一行代码却能处理如此强大、复杂的功能:汇总、拼接、累加计算、分组等。

切记,不要用错哦,是java.util.stream.Collectors,不是java.util.Collections。

/**
  * Implementations of {@link Collector} that implement various useful reduction
  * operations, such as accumulating elements into collections, summarizing
  * elements according to various criteria, etc.
  *
  * <p>The following are examples of using the predefined collectors to perform
  * common mutable reduction tasks:
  *
  * <pre>{@code
  *     // Accumulate names into a List
  *     List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
  *
  *     // Accumulate names into a TreeSet
  *     Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));
  *
  *     // Convert elements to strings and concatenate them, separated by commas
  *     String joined = things.stream()
  *                           .map(Object::toString)
  *                           .collect(Collectors.joining(", "));
  *
  *     // Compute sum of salaries of employee
  *     int total = employees.stream()
  *                          .collect(Collectors.summingInt(Employee::getSalary)));
  *
  *     // Group employees by department
  *     Map<Department, List<Employee>> byDept
  *         = employees.stream()
  *                    .collect(Collectors.groupingBy(Employee::getDepartment));
  *
  *     // Compute sum of salaries by department
  *     Map<Department, Integer> totalByDept
  *         = employees.stream()
  *                    .collect(Collectors.groupingBy(Employee::getDepartment,
  *                                                   Collectors.summingInt(Employee::getSalary)));
  *
  *     // Partition students into passing and failing
  *     Map<Boolean, List<Student>> passingFailing =
  *         students.stream()
  *                 .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
  *
  * }</pre>
  *
  * @since 1.8
  */



换句话说,Collectors结合Stream将成为集合的终极操作,其中,包括:

类型归类:将集合中元素按照类型、条件过滤等归类,存放到指定类型的新集合。
分组:按照条件对元素进行分组,和SQL中group by的用法有异曲同工之妙。
分区:分组的特殊情况,实质是在做二分组,将符合条件、不符合条件的元素分组到两个key分别为true和false的Map中,从而我们能够得到符合和不符合的分组新集合。
最值:按照某个属性查找最大、最小元素。
累加、汇总:用来完成累加计算、数据汇总(总数、总和、最小值、最大值、平均值)。
连接:将元素以某种规则连接起来。
……
二、实战演练
1. 类型归类
将集合中元素按照类型、条件过滤等归类,存放到指定类型的新集合,List、Map、Set、Collection或者ConcurrentMap。涉及以下方法:

Collectors.toList()
Collectors.toMap()
Collectors.toSet()
Collectors.toCollection()
Collectors.toConcurrentMap()

一般都作为终止操作符cololect的参数来使用,并伴随着流的结束。

常用于收集、筛选出集合(复杂集合)中的符合条件的数据,并存放于对应类型的新集合中,便于后续实际业务逻辑处理。

比如,将名字类型归类存在到List<String>集合中:

List<String> list = allPeoples.stream().map(People::getName).collect(Collectors.toList());



2. 分组
按照条件对元素进行分组,和 SQL 中的 group by 用法有异曲同工之妙,通常也建议使用Java代码进行分组处理以减轻数据库SQL压力。

分组涉及以下方法:

Collectors.groupingBy(…):普通分组。

Collectors.groupingByConcurrent(…):线程安全的分组。

分组后,返回的是一个Map集合,其中key作为分组对象,value作为对应分组结果。

比如,考虑到People集合中可能会存在同龄人,将集合按照年龄进行分组:

Map<Integer, List<People>> groupingByAge = allPeoples.stream().collect(Collectors.groupingBy(People::getAge));

如果我们不想返回Map的value为List怎么办?实际上可以按照下面的方式分组:

Map<Integer, Set<People>> groupingByAge2 = allPeoples.stream().collect(Collectors.groupingBy(People::getAge, Collectors.toSet()));

考虑到同步安全问题时,怎么办?

采用线程安全的分组Collectors.groupingByConcurrent(…),于是:

Map<Integer, List<People>> groupingByAge3 = allPeoples.stream().collect(Collectors.groupingByConcurrent(People::getAge));

3. 分区
是分组的特殊情况,采用Collectors.partitioningBy(…)方法来完成。

该方法实质是在做二分组,将符合条件、不符合条件的元素分组到两个key分别为true和false的Map中,从而我们能够得到符合和不符合的分组新集合。

比如,People集合中人名有中文名,也有英文名,将人名按照中、英文名进行分区:

Map<Boolean, List<People>> partitioningByName = allPeoples.stream().collect(Collectors.partitioningBy(people -> people.getName().matches("^[a-zA-Z]*")));
 // 获取英文名集合
 List<People> englishNames = partitioningByName.get(true);
 // 获取中文名集合
 List<People> chineseNames = partitioningByName.get(false);

更多请见:http://www.mark-to-win.com/tutorial/50329.html