Java 流的高级使用之收集数据解析

目录
  • 一、前言
    • 1.1 收集器
    • 1.2 预定义收集器
      • Collectors类为我们提供的收集器,主要包含三大功能:
  • 二、深入
    • 2.1 规约和汇总
      • 统计元素数量
      • 查找流中的最大值和最小值
      • 汇总
    • 连接字符串
    • 2.2 分组

一、前言

1.1 收集器

收集器的接口是java.util.stream.Collector,我们只需要调用流的collect方法并传递给一个Collector接口的一个实现(也就是给Stream中元素做汇总的方法),就可以了。例如java.util.stream.Collectors类的toList()方法,该方法就会返回一个按顺序给每个元素生成一个列表的Collector接口的实现。

收集器非常有用,因为它可以简介而灵活地定义collect用来生成结果集合的标准。更具体地说,对流调用collect方法将对流中的元素触发一个规约操作(由Collector来参数化)。

1.2 预定义收集器

JDK为我们提供了java.util.stream.Collectors类,其为我们提供了很多静态工厂方法,可以方便地创建常见的收集器实例,而我们只要拿来用就可以了。最直接和最常用的收集器是toList静态方法,它会把流中所有的元素收集到一个List中。

Collectors类为我们提供的收集器,主要包含三大功能:

  • 将流元素规约和汇总为一个值
  • 元素分组
  • 元素分区

注意:因为其为我们提供的都是静态方法,我们可以通过静态导入的方式简化代码的书写。

二、深入

2.1 规约和汇总

统计元素数量

Collectors为我们提供了counting方法,为我们提供了统计元素数量的收集器。实例:

long howManyDishes = menu.stream().collect(Collectors.counting());

上面的示例是利用预定义收集器实现的,其实Stream接口定义了count方法,因此我们也可以直接调用Stream提供的预定义方法来实现,如下:

long howManyDishes = menu.stream().count();

功能一样用哪个才好呢?其实如果你的需求只是统计流中元素的数量的时候,二者皆可。最大的区别在于count()是一个终端操作,而counting返回的是一个收集器,其可以和其它收集器联合使用。

查找流中的最大值和最小值

Collectors为我们提供了maxBy方法和mixBy方法,为我们提供了计算流中的最大或最小值的收集器。实例:

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));

汇总

求和

Collectors类专门为汇总提供了一个工厂方法: Collectors.summingInt。它可接受一个把对象映射为求和所需int的函数,并返回一个收集器;该收集器在传递给普通的collect方法后即执行我们需要的汇总操作。

类Collectors.summingLong和Collectors.summingDouble方法的作用完全一样,可以用于求和字段为long或double的情况。

求出菜单列表的总热量的示例:

int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

上面代码的收集过程下图所示。在遍历流时,会把每一道菜都映射为其热量,然后把这个数字累加到一个累加器(这里的初始值0)。

平均值

Collectors类的averagingInt、averagingLong 和 averagingDouble 可以为我们生成计算数值的平均数的收集器:

double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories));

综合汇总

Collectors类为我们提供了summarizingInt工厂方法,其返回的收集器可以一次性统计出总数、总和、平均值、最大值和最小值。

例如,通过一次summarizing操作你可以就数出菜单中元素的个数,并得到菜肴热量总和、平均值、最大值和最小值:

IntSummaryStatistics menuStatistics =
        menu.stream().collect(summarizingInt(Dish::getCalories));

这个收集器会把所有这些信息收集到一个叫作IntSummaryStatistics的类里,它提供了方便的取值方法来访问结果。打印menuStatisticobject会得到以下输出:

IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}

同样,相应的summarizingLong和summarizingDouble工厂方法有相关的LongSummaryStatistics 和DoubleSummaryStatistics 类 型 , 适用于收集的属性是原始类型 long 或 double 的情况。

连接字符串

Collectors类为我们提供的joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。

这意味着你把菜单中所有菜肴的名称连接起来,如下所示:

String shortMenu = menu.stream().map(Dish::getName).collect(joining());

注意: joining在内部使用了StringBuilder来把生成的字符串逐个追加起来。

此外joining工厂方法有一个重载版本可以接受元素之间的分界符,这样你就可以得到一个逗号分隔的菜肴名称列表:

String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));

广义的规约汇总

前面所提及的收集器都是一个可以用reducing工厂方法定义的规约过程的特殊情况而已。Collectors.reducing工厂方法是所有这些特殊情况的一般化。

public static <T,U> Collector<T,?,U> reducing(U identity,
                    Function<? super T,? extends U> mapper, BinaryOperator<U> op)

参数解析:

  • 第一个参数时规约操作的起始值,也是流中没有元素时的返回值。
  • 第二个参数是Function,将做一定的转换操作。
  • 第三个参数BinaryOperator,将两个项目累积成一个同类型的值。

我们将上面的示例转换一下:

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));
// 转换
Optional<Dish> mostCalorieDish =
        menu.stream().collect(reducing(
                (d1,d2) -> d1.getCalories() > d1.getCalories() ? d1 : d2));

上面转换示例中,我们使用的是一个单参数的reducing工厂方法创建的收集器,其可以看做是三个参数方法的特殊情况,它把流中的第一个项目作为起点,把恒等函数(即一个函数仅仅是返回其输入参数)作为一个转换函数。

2.2 分组

一个常见的数据库操作是根据一个或多个属性对集合中的项目进行分组。

假设你要把菜单中的菜按照类型进行分类,有肉的放一组,有鱼的放一组,其他的都放另一组。用Collectors.groupingBy工厂方法返回的收集器就可以轻松地完成这项任务,如下所示:

Map<Dish.Type, List<Dish>> dishesByType =
        menu.stream().collect(groupingBy(Dish::getType));

这里,你给groupingBy方法传递了一个Function(以方法引用的形式),它提取了流中每一道Dish的Dish.Type。我们把这个Function叫作分类函数,因为它用来把流中的元素分成不同的组。如下图所示,分组操作的结果是一个Map,把分组函数返回的值作为映射的键,把流中所有具有这个分类值的项目的列表作为对应的映射值。在菜单分类的例子中,键就是菜的类型,值就是包含所有对应类型的菜肴的列表。

特殊应用示例:

public enum CaloricLevel{DIET,NORMAL,FAT}
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(groupingBy(dish -> {
    if (dish.getCalories() <= 400)
        return CaloricLevel.DIET;
    else if (dish.getCalories() <= 700)
        return CaloricLevel.NORMAL;
    else
        return CaloricLevel.FAT;
}));

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java常用数据流应用实例解析

    这篇文章主要介绍了java常用数据流应用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 按操作单位的不同分为:字节流(8bit)(InputStream.OuputStream).字符流(16bit)(Reader.Writer) 按数据流的流向不同分为:输入流.输出流 按角色的不同分为:节点流.处理流 一.不带缓冲的流 1.文件字节输入流.文件字节输出流 package anno; import java.io.File; impor

  • Java流操作之数据流实例代码

    实例1: package dataInputStreamAndPrintStreamDemo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintStream; //示范如何自键

  • 基于Java8 Stream API实现数据抽取收集

    目标&背景 我们以"处理订单数据"为例,假设我们的应用是一个分布式应用,有"订单应用","物流应用","商品应用"等都是独立的服务.本次我们的目的需要展示订单列表完整数据: 1.查询订单列表. 2.批量查询物流信息. 3.将物流信息填充到订单主信息中. 假设我们定义了一个订单类,具有几个关键的属性:订单号,状态,订单价,快递信息.如下所示: class Order{ String orderSeq; String st

  • 详解java8中的Stream数据流

    Stream是java8引入的一个重度使用lambda表达式的API.Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象.直观意味着开发者在写代码时只需关注他们想要的结果是什么而无需关注实现结果的具体方式.这一章节中,我们将介绍为什么我们需要一种新的数据处理API.Collection和Stream的不同之处以及如何将StreamAPI应用到我们的编码中. 筛选重复的元素 Stream 接口支持 distinct 的方法, 它会返回一个元素

  • Java输入输出流的使用详细介绍

    1.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列.Java的I/O流提供了读写数据的标准方法.任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法. Java.io是大多数面向数据流的输入/输出类的主要软件包.此外,Java也对块传输提供支持,在核心库 java.nio中采用的便是块IO. 流IO的好处是简单易用,缺点是效率较低.块IO效率很高,但编程比较

  • Java 流的高级使用之收集数据解析

    目录 一.前言 1.1 收集器 1.2 预定义收集器 Collectors类为我们提供的收集器,主要包含三大功能: 二.深入 2.1 规约和汇总 统计元素数量 查找流中的最大值和最小值 汇总 连接字符串 2.2 分组 一.前言 1.1 收集器 收集器的接口是java.util.stream.Collector,我们只需要调用流的collect方法并传递给一个Collector接口的一个实现(也就是给Stream中元素做汇总的方法),就可以了.例如java.util.stream.Collecto

  • Java 流处理之收集器详解

    目录 收集所有记录的 列1 值,以列表形式存储结果 收集所有记录的 列1 值,且去重,以集合形式存储 收集记录的 列2 值和 列3 值的对应关系,以字典形式存储 收集所有记录中 列3 值最大的记录 收集所有记录中 列3 值的总和 创建一个中间结果容器 逐一遍历流中的每个元素,处理完成之后,添加到中间结果 中间结果转换成最终结果 combiner()是做什么的? characteristics()是什么的? 完整代码 Java 流(Stream)处理操作完成之后,我们可以收集这个流中的元素,使之汇

  • java中RabbitMQ高级应用

    目录 1.消息可靠性投递 1.1.确认模式 1.2.退回模式 1.3.确认机制 2.消费端限流 3.消息过期时间 4.死信队列 4.1.死信概念 4.2.延迟队列 1.消息可靠性投递  在使用 RabbitMQ 的时候,生产者在进行消息投递的时候如果想知道消息是否成功的投递到对应的交换机和队列中,有两种方式可以用来控制消息投递的可靠性模式 .  由上图的整个消息的投递过程来看,生产者的消息进入到中间件中会首先到达交换机,然后再从交换机传递到队列中去,也就是分为两步走战略.那么消息的丢失情况也就是

  • java使用Socket类接收和发送数据

    网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类.通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据.在本文及后面的数篇文章中将详细讨论Socket类的使用,内容包括Socket类基础.各式各样的连接方式.get和set方法.连接过程中的超时以及关闭网络连接等. 在本文中,我们将讨论使用Socket类的基本步骤和方法.一般网络客户端程序在连接服务程序时要进行以下三步操作. 连接服务器 发送和接收数据 关闭网络连接 一.连接服务器 在

  • Java使用poi组件导出Excel格式数据

    在做管理系统的时候,我想Excel的导出是我们很难规避掉的,而且这也是个很实用很人性化的功能. Java中对于Excel的支持有很多种,比如说JXL,POI等.我这边使用的是POI进行一个Excel的操作,下面我会简单分享下POI组件的使用,以及我使用比较多一个工具类. POI组件 poi组件是由Apache提供的组件包,主要职责是为我们的Java程序提供对于office文档的相关操作.本文主要是它对于Excel操作的一个介绍. 官方主页:http://poi.apache.org/index.

  • 如何去除Java中List集合中的重复数据

    1.循环list中的所有元素然后删除重复 public class duplicatRemoval { public static List removeDuplicate(List list){ for(int i=0;i<list. size()-1;i++){ for(int j=list.size()-1;j>i;j--){ if(list. get(j). equals(list.get(i))){ list.remove(j); } } } return list; } } 总结:

  • java 流操作对文件的分割和合并的实例详解

    java 流操作对文件的分割和合并的实例详解 学习文件的输入输出流,自己做一个小的示例,对文件进行分割和合并. 下面是代码: package com.dufy.file; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.ut

  • 详解Java虚拟机管理的内存运行时数据区域

    详解Java虚拟机管理的内存运行时数据区域 概述 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁. 程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基

  • java 结合jQuery实现跨域名获取数据的方法

    一.什么是跨域? 由于浏览器出于安全的考虑,采取了同源策略的限制,使得jQuery无法直接跨域名互相操作对象或数据.例如:a.com 域名下的 a.html页面利用jQuery无法操作b.com 域名下b.html页面的对象或是数据, 并且默认情况下也不能操作test.a.com域名下的 test.html的 对象或是数据 .只要满足下面条件的jQuery都会视为跨域名: 1.主域相同,子域不同,如xxx.aaa.com和yyy.aaa.com 2.域名相同,端口不同,如xxx.aaa.com:

随机推荐