Java™ 教程(聚合归纳操作)

强转星轨
• 阅读 1905

聚合归纳操作

聚合操作一节描述了下列操作管道,计算集合roster中所有男性成员的平均年龄:

double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();

JDK包含许多终端操作(比如averagesumminmaxcount),它们通过组合流的内容返回一个值,这些操作称为归纳操作。JDK还包含返回一个集合而不是单个值的归纳操作,许多归纳操作执行特定的任务,例如查找值的平均值或将元素分组到类别中。但是,JDK为你提供了通用的reducecollect操作,本节将详细介绍这些操作。

你可以在示例ReductionExamples中找到本节中描述的代码摘录。

Stream.reduce方法

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来实现这一点。

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操作接受三个参数:

  • suppliersupplier是个工厂方法,它构造新的实例,对于collect操作,它创建结果容器的实例,在本例中,它是Averager类的一个新实例。
  • accumulatoraccumulator函数将流元素合并到结果容器中,在本例中,它通过将count变量增加1,并将流元素的值添加到total成员变量中,该元素是一个整数,表示男性成员的年龄,来修改Averager结果容器。
  • combinercombiner函数接受两个结果容器并合并它们的内容,在本例中,它通过将count变量与另一个Averager实例的count成员变量相加,并将另一个Averager实例的total成员变量的值添加到total成员变量中,从而修改Averager结果容器。

请注意以下:

  • supplier是lambda表达式(或方法引用),而不是reduce操作中的identity元素之类的值。
  • accumulatorcombiner函数不返回值。
  • 你可以使用并行流的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操作中用作参数的函数,该操作需要三个参数(supplieraccumulatorcombiner函数)。

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.MALEPerson.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,这是年龄总和的初始值,如果不存在成员,则为默认值。
  • mapperreducing操作将此mapper函数应用于所有流元素,在本例中,mapper检索每个成员的年龄。
  • operationoperation函数用于归纳映射值,在本例中,operation函数添加Integer值。

下面的例子检索了每种性别成员的平均年龄:

Map<Person.Sex, Double> averageAgeByGender = roster
    .stream()
    .collect(
        Collectors.groupingBy(
            Person::getGender,                      
            Collectors.averagingInt(Person::getAge)));

上一篇:聚合操作
点赞
收藏
评论区
推荐文章
Karen110 Karen110
4年前
一篇文章带你了解Django ORM操作(高端篇)
前言上次两篇基本学完的DjangoORM各种操作,怎么查,各种查。感兴趣的小伙伴可以戳这两篇文章学习下,、。但是还是遗留了一些技能。,再来瞅瞅吧!查询聚合操作聚合操作,不要被名字吓到了,通常用在筛选完一些数据之后,求一下平均值了,什么的。例如:求所有书的总价格和平均价格原生sqlSELECTSUM(price)AS"所有书总价格",a
Wesley13 Wesley13
3年前
ES聚合使用
一.聚合查询分类:聚合方式说明MetricAggregation(指标聚合)一些数学计算,可以对文档字段统计分析BucketAggregation(桶聚合)一些满足特定条件的文档的集合PipelineAggregation(管道聚合)对其他的聚合结果进行二次聚合MetrixAggregation
Stella981 Stella981
3年前
ES Pipeline Aggregation(管道聚合)
微信公众号:\中间件兴趣圈\关于作者:《RocketMQ技术内幕》作者;管道聚合处理来自其他聚合而不是文档集的输出,将信息添加到输出树中。注:关于脚本聚合目前在本文中暂时不会涉及。主要有如下两种管道聚合方式:parentsibling下面一一介绍ES定义的管道聚合。
Wesley13 Wesley13
3年前
JDK8之lambda表达式
/JDK8Stream特性Createdbychengbxon2018/5/27.Java8中的Stream是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregateoperation),或者大批量数据操
Wesley13 Wesley13
3年前
Java8 新特性之集合操作Stream
Java8新特性之集合操作StreamStream简介Java8引入了全新的StreamAPI。这里的Stream和I/O流不同,它更像具有Iterable的集合类,但行为和集合类又有所不同。stream是对集合对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。
Wesley13 Wesley13
3年前
Java8 数据流
一、基本知识\\数据流(stream)\\是对集合(collection)功能的增强,更专注于对集合对象的各种便利、高效的聚合,大批量数据操作。数据流的特点:元素序列流提供了一组特定类型的以顺序方式元素。源流使用集合,数组或I/O资源为输入源。聚合操作数据流支持如filter
Wesley13 Wesley13
3年前
Java 8 Stream API学习总结
Java8API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。StreamAPI可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。元素流在管道中经过中间操作(intermediateo
Stella981 Stella981
3年前
C#操作mongodb(聚合函数)
 publicstaticvoidOnQuery\_QXData(stringDBName,stringtablename,stringlayername)       {           if(ConnectionStringnull)OnCreateDB();           MoDataBase
Wesley13 Wesley13
3年前
MongoDB系列
 MongoDB中聚合(aggregate) 操作将来自多个document的value组合在一起,并通过对分组数据进行各种操作处理,并返回计算后的数据结果,主要用于处理数据(诸如统计平均值,求和等)。MongoDB提供三种方式去执行聚合操作:聚合管道(aggregationpipeline)、MapReduce函数以及单一的聚合命令(count、di
Stella981 Stella981
3年前
Elasticsearch学习总结八 ElasticSearch中的聚合操作
首先准备数据,索引包含四个字段fieldA,fieldB,fieldC,fieldD,如下图,以下案列中都使用了基本REST命令和JavaAP两种方式实现!输入图片说明(https://static.oschina.net/uploads/img/201706/15212621_M2dc.png"在这里输入图片标题")1).首先按照某
3A网络 3A网络
2年前
天天写 SQL,这些神奇的特性你知道吗?
天天写SQL,这些神奇的特性你知道吗?一SQL的第一个神奇特性日常开发我们经常会对表进行聚合查询操作,但只能在SELECT子句中写下面3种内容:通过GROUPBY子句指定的聚合键、聚合函数(SUM、AVG等)、常量,不懂没关系我们来看个例子听我解释有学生班级表(tblstudentclass)以及数据如下textDROPTAB
强转星轨
强转星轨
Lv1
人面不知何处去,桃花依旧笑春风。
文章
4
粉丝
0
获赞
0