我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

一、前言

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;
}));

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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java集合框架全解析:解剖数据结构,揭秘高效存储之道

Java集合框架为开发人员提供了丰富的的数据结构,可以满足不同的数据存储和处理需求。本文将对Java集合框架进行全面的解析,从数据结构的角度剖析其内部实现,揭示高效存储的奥秘。
Java集合框架全解析:解剖数据结构,揭秘高效存储之道
2024-02-22

【已解决】Java 中使用 ES 高级客户端库 RestHighLevelClient 清理百万级规模历史数据

🎉工作中遇到这样一个需求场景:由于ES数据库中历史数据过多,占用太多的磁盘空间,需要定期地进行清理,在一定程度上可以释放磁盘空间,减轻磁盘空间压力。 🎈在经过调研之后发现,某服务项目每周产生的数据量已经
2023-08-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录