聚合操作一节描述了下列操作管道,计算集合		roster
中所有男性成员的平均年龄:	
double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();
	JDK包含许多终端操作(比如 average 、 sum 、 min 、 max 和 count ),它们通过组合流的内容返回一个值,这些操作称为归纳操作。JDK还包含返回一个集合而不是单个值的归纳操作,许多归纳操作执行特定的任务,例如查找值的平均值或将元素分组到类别中。但是,JDK为你提供了通用的 reduce 和 collect 操作,本节将详细介绍这些操作。
你可以在示例 ReductionExamples 中找到本节中描述的代码摘录。
		Stream.reduce
方法是一种通用的归纳操作,考虑以下管道,它计算集合		roster
中男性成员的年龄总和,它使用		Stream.sum
归纳操作。	
Integer totalAge = roster
    .stream()
    .mapToInt(Person::getAge)
    .sum();
	
将其与下面使用流的管道进行比较,使用		Stream.reduce
操作计算相同的值:	
Integer totalAgeReduce = roster
   .stream()
   .map(Person::getAge)
   .reduce(
       0,
       (a, b) -> a + b);
	
本例中的		reduce
操作有两个参数:	
identity
:标识元素既是归纳的初始值,也是流中没有元素时的默认结果,在本例中,标示元素为			0
,这是年龄总和的初始值,如果集合			roster
中没有成员,则为默认值。		accumulator
:累加器函数接受两个参数:归纳的部分结果(在本例中,是到目前为止所有处理过的整数的和)和流的下一个元素(在本例中,是一个整数),它返回一个新的局部结果。在本例中,累加器函数是一个lambda表达式,它添加两个			Integer
值并返回一个			Integer
值:			(a, b) -> a + b
。			reduce
操作总是返回一个新值,但是,			accumulator
函数每次处理流的元素时也会返回一个新值,假设你希望将流的元素归纳为更复杂的对象,例如集合,这可能会影响应用程序的性能。如果			reduce
操作涉及向集合添加元素,那么每次累加器函数处理一个元素时,它都会创建一个包含该元素的新集合,这是低效的,相反,更新现有的集合将更有效,你可以使用			Stream.collect
来实现这一点。		
与		reduce
方法不同,		collect
方法修改或改变现有值,而		reduce
方法在处理元素时总是创建一个新值。	
考虑如何找到流中值的平均值,你需要两段数据:值的总数和这些值的和。然而,与		reduce
方法和所有其他归纳方法一样,		collect
方法只返回一个值,你可以创建一个包含成员变量的新数据类型,该成员变量跟踪值的总数和这些值的总和,例如下面的类		Averager
:	
class Averager implements IntConsumer {
    private int total = 0;
    private int count = 0;
        
    public double average() {
        return count > 0 ? ((double) total)/count : 0;
    }
        
    public void accept(int i) { total += i; count++; }
    public void combine(Averager other) {
        total += other.total;
        count += other.count;
    }
}
	
下面的管道使用		Averager
类和		collect
方法计算所有男性成员的平均年龄:	
Averager averageCollect = roster.stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .map(Person::getAge)
    .collect(Averager::new, Averager::accept, Averager::combine);
                   
System.out.println("Average age of male members: " +
    averageCollect.average());
	
本例中的		collect
操作接受三个参数:	
supplier
:			supplier
是个工厂方法,它构造新的实例,对于			collect
操作,它创建结果容器的实例,在本例中,它是			Averager
类的一个新实例。		accumulator
:			accumulator
函数将流元素合并到结果容器中,在本例中,它通过将			count
变量增加1,并将流元素的值添加到			total
成员变量中,该元素是一个整数,表示男性成员的年龄,来修改			Averager
结果容器。		combiner
:			combiner
函数接受两个结果容器并合并它们的内容,在本例中,它通过将			count
变量与另一个			Averager
实例的			count
成员变量相加,并将另一个			Averager
实例的			total
成员变量的值添加到			total
成员变量中,从而修改			Averager
结果容器。		请注意以下:
supplier
是lambda表达式(或方法引用),而不是			reduce
操作中的			identity
元素之类的值。		accumulator
和			combiner
函数不返回值。		collect
操作(如果你使用并行流运行			collect
方法,那么每当			combiner
函数创建一个新对象时,JDK都会创建一个新线程,例如本例中的			Averager
对象,因此,你不必担心同步)。		
虽然JDK提供了计算流中元素平均值的		average
操作,但是如果需要从流的元素中计算多个值,可以使用		collect
操作和自定义类。	
		collect
操作最适合于集合,下面的示例使用		collect
操作将男性成员的名称放入集合中:	
List<String> namesOfMaleMembersCollect = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .map(p -> p.getName())
    .collect(Collectors.toList());
	
这个版本的		collect
操作只接受		Collector
类型的一个参数,该类封装了		collect
操作中用作参数的函数,该操作需要三个参数(		supplier
、		accumulator
和		combiner
函数)。	
		Collectors
类包含许多有用的归纳操作,比如将元素累积到集合中,并根据各种标准汇总元素,这些归纳操作返回类		Collector
的实例,因此可以将它们用作		collect
操作的参数。	
本例使用		Collectors.toList
操作,它将流元素累积到		List
的新实例中,与		Collectors
类中的大多数操作一样,		toList
操作符返回		Collector
的实例,而不是集合。	
以下示例按性别将集合		roster
的成员分组:	
Map<Person.Sex, List<Person>> byGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(Person::getGender));
	
		groupingBy
操作返回一个		map
,其键是应用指定为其参数的lambda表达式(称为分类函数)所得到的值。在本例中,返回的		map
包含两个键,		Person.Sex.MALE
和		Person.Sex.FEMALE
,键对应的值是		List
的实例,其中包含流元素,当分类函数处理这些元素时,这些元素与键值对应。例如,与键		Person.Sex.MALE
对应的值是一个包含所有男性成员的		List
实例。	
以下示例检索集合		roster
中每个成员的姓名,并按性别将其分组:	
Map<Person.Sex, List<String>> namesByGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.mapping(
                    Person::getName,
                    Collectors.toList())));
	
本例中的		groupingBy
操作接受两个参数,一个分类函数和一个		Collector
实例,		Collector
参数称为下游收集器,这是Java运行时应用于另一个收集器的结果的收集器。因此,这个		groupingBy
操作使你能够对		groupingBy
操作符创建的		List
值应用		collect
方法。此示例应用收集器		mapping
,它将		mapping
函数		Person::getName
应用于流的每个元素。因此,产生的流只包含成员的名称,包含一个或多个下游收集器的管道(如本例)称为多级归纳。	
下面的示例检索每种性别成员的总年龄:
Map<Person.Sex, Integer> totalAgeByGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.reducing(
                    0,
                    Person::getAge,
                    Integer::sum)));
	reducing 操作需要三个参数:
identity
:如			Stream.reduce
操作,如果流中没有元素,则			identity
元素既是归纳的初始值,也是缺省结果,在这个例子中,			identity
元素是			0
,这是年龄总和的初始值,如果不存在成员,则为默认值。		mapper
:			reducing
操作将此			mapper
函数应用于所有流元素,在本例中,			mapper
检索每个成员的年龄。		operation
:			operation
函数用于归纳映射值,在本例中,			operation
函数添加			Integer
值。		下面的例子检索了每种性别成员的平均年龄:
Map<Person.Sex, Double> averageAgeByGender = roster
    .stream()
    .collect(
        Collectors.groupingBy(
            Person::getGender,                      
            Collectors.averagingInt(Person::getAge)));
	上一篇:聚合操作