深入了解Java 8 新特性:Stream流的实践应用(二)

阅读建议

嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:

  1. 本篇文章大概8000多字,预计阅读时间长需要10分钟(不要害怕字数过多,其中有一大部分是示例代码,读起来是比较轻松的)。
  2. 本篇文章兼具实战性和理论性,是一篇质量分数较高的技术干货文章,建议收藏起来,方便时常学习与回顾,温故而知新。
  3. 创作不易,免费的点赞、关注,请走上一走,算是对博主一些鼓励,让我更有动力输出更多的干货内容。

Collectors类的核心方法

        Stream是一个数据结构,它提供了一种方便的方式来处理和操作数据,Stream.collect() 方法则用于将 Stream 中的元素收集到指定的收集器(Collector)中,返回一个结果对象。收集器(Collector)可以是任何实现了 Collector 接口的对象,用于定义如何将 Stream 中的元素收集到结果对象中。而Collectors可以看作是收集器(Collector)的生产工厂,可以调用具体的静态方法返回一个具体的收集器。常用的如下:

Collectors#toCollection

        Collectors类的toCollection()方法用于将Stream中的元素收集到新的集合中。这个方法的功能作用是接收一个输入流并产生一个输出结果,即把Stream中的元素收集到某种类型的集合中,这里的某类类型集合可由用户以lambda表达式来定义,如:

  1. 收集元素到ArrayList:通过调用toCollection()方法和传递ArrayList类型的构造方法,可以将Stream中的元素收集到ArrayList中。
  2. 收集元素到HashSet:通过调用toCollection()方法和传递HashSet类型的构造方法,可以将Stream中的元素收集到HashSet中。
  3. 收集元素到LinkedHashSet:通过调用toCollection()方法和传递LinkedHashSet类型的构造方法,可以将Stream中的元素收集到LinkedHashSet中。
  4. 收集元素到TreeSet:通过调用toCollection()方法和传递TreeSet类型的构造方法,可以将Stream中的元素收集到TreeSet中。
  5. 收集元素到List或Set:通过调用toCollection()方法和传递适当的集合类型构造方法,可以将Stream中的元素收集到List或Set中。

示例

@Test
public void test22(){
    Stream<String> stream = Stream.of("zhangsan", "lisi", "wangwu", "zhaoliu");
    HashSet<String> set = stream.collect(Collectors.toCollection(() -> new HashSet<String>()));
    System.out.println(set.toString());
}

Collectors#toList

        Collectors.toList() 用于将 Stream 中的元素收集到一个新的 List 中。这个方法返回一个 Collector 对象,该对象定义了如何将 Stream 中的元素收集到 List 中。

Collectors#toUnmodifiableList

        Collectors.toUnmodifiableList() 用于将 Stream 中的元素收集到一个新的不可修改的 List 中。这个方法返回一个 Collector 对象,该对象定义了如何将 Stream 中的元素收集到一个不可修改的 List 中。与Collectors#toList区别就是返回的 List 是不可修改的,无法被重新赋值或添加/删除元素。

Collectors#toSet

        Collectors.toSet() 用于将 Stream 中的元素收集到一个新的 Set 中。这个方法返回一个 Collector 对象,该对象定义了如何将 Stream 中的元素收集到 Set 中。

        需要注意的是,Collectors.toSet() 方法返回的 Collector 对象可以处理任意类型的 Stream,包括泛型 Stream。这意味着您可以使用该方法将任何类型的元素收集到 Set 中。另外,由于 Set 本身不允许存在重复的元素,因此 Collectors.toSet() 方法会自动去重,只保留唯一的元素。

Collectors#toUnmodifiableSet

        Collectors.toUnmodifiableSet()用于将 Stream 中的元素收集到一个新的、不可修改的 Set 中。这个方法返回一个 Collector 对象,该对象定义了如何将 Stream 中的元素收集到一个不可修改的 Set 中。

        需要注意的是,由于返回的 Set 是不可修改的,因此不能添加或删除元素。这可以确保收集到的元素不会在后续操作中被修改。此外,与 Collectors.toList() 方法类似,toUnmodifiableSet() 方法也可以处理任意类型的 Stream,包括泛型 Stream。

Collectors#joining

        Collectors.joining() 方法用于连接多个元素。该方法返回一个 Collector 实例,方便在流收集器上进行链式操作。Collectors.joining() 方法以遭遇元素的顺序拼接元素。我们可以传递自定义的拼接字符串、前缀和后缀。

@Test
public void test23() {
    Stream<String> stream = Stream.of("zhangsan", "lisi", "wangwu", "zhaoliu");
    String string = stream.collect(Collectors.joining("-"));
    System.out.println(string);//输出结果:zhangsan-lisi-wangwu-zhaoliu
}

Collectors#counting

        Collectors.counting()用于计算Stream中元素的数量。它会遍历输入的Stream,对每个元素进行计数,并返回一个Long类型的值,表示元素的数量。

@Test
public void test24() {
    Stream<String> stream = Stream.of("zhangsan", "lisi", "wangwu", "zhaoliu");;
    Long count = stream.filter(item->item.startsWith("a")).collect(Collectors.counting());
    System.out.println(count);
}

Collectors#minBy

        Collectors.minBy()用于在Stream中的元素上执行最小值计算操作。该方法方法接受一个比较器作为参数,并将该比较器用于Stream中的元素。它会计算每个元素的最小值,并返回一个包含最小值元素的Optional对,Optional对象包装了最小值元素。如果Stream为空,则返回的Optional对象为空(empty)。

@Test
public void test25() {
    Stream<Integer> stream = Stream.of(1, 6, 29, 45, 2);
    Integer min = stream.collect(Collectors.minBy((v1, v2) -> {
        if (v1 > v2) {
            return 1;
        } else if (v1 < v2) {
            return -1;
        } else {
            return 0;
        }
    })).get();
    System.out.println(min);//输出结果为1
}

Collectors#maxBy

        Collectors.maxBy()用于将Stream中的元素进行比较并找出最大值。该方法接受一个比较函数作为参数,该比较函数定义了如何比较Stream中的元素。这个比较函数可以是自定义的,例如比较两个字符串的长度、比较两个日期的日期值等,通过比较操作可以找出Stream中最大的元素。

@Test
public void test26() {
    Stream<Integer> stream = Stream.of(1, 6, 29, 45, 2);
    Integer min = stream.collect(Collectors.minBy((v1, v2) -> {
        if (v1 > v2) {
            return -1;
        } else if (v1 < v2) {
            return 1;
        } else {
            return 0;
        }
    })).get();
    System.out.println(min);//输出结果为45
}

Collectors#summingInt

        Collectors.summingInt()用于将Stream中的元素进行求和操作。该方法的功能作用是接受一个将对象映射为int的函数,并返回一个收集器,用于计算元素的和。在传递给普通的collect方法后,该收集器即执行所需的汇总操作,计算元素的和。

@Test
public void test26() {
    Stream<Integer> stream = Stream.of(10, 20, 30, 40, 50);
    Integer sum = stream.collect(Collectors.summingInt(item -> item));
    System.out.println(sum);//输出结果为150
}

Collectors#averagingInt

        Collectors.averagingInt()方法的功能作用是计算数值的平均数。它接受一个将对象映射为求和所需的int的函数,并返回一个收集器。在传递给普通的collect方法后,该收集器即执行所需的汇总操作,计算元素的平均值。

@Test
public void test27() {
    Stream<Integer> stream = Stream.of(2, 4, 6);
    double sum = stream.collect(Collectors.averagingInt(item->item));
    System.out.println(sum);//输出结果为4.0
}

Collectors#reducing

        Collectors.reducing()方法的功能作用是对Stream中的元素进行归约操作,将Stream中的元素聚合为单一的值。该方法接受一个BinaryOperator作为参数,用于指定如何将两个元素进行归约操作。

@Test
public void test28() {
    Stream<Integer> stream = Stream.of(2, 4, 6);
    Integer sum = stream.collect(Collectors.reducing(Integer::sum)).get();
    System.out.println(sum);
    Stream<Integer> stream2 = Stream.of(1, 2, 3);
    Optional<Integer> optional = stream2.collect(Collectors.reducing((v1, v2) -> v1 + v2));
    Integer sum2 = optional.get();
    System.out.println(sum2);
}

Collectors#groupingBy

        Collectors.groupingBy()用于将Stream中的元素进行分组操作。具体来说,先通过传递一个分类函数作为参数,Collectors.groupingBy()方法可以将Stream中的元素按照该函数的返回值进行分组。这个分类函数接受一个元素作为输入,并返回一个用于分组的键。然后Collectors.groupingBy()方法返回一个Collector对象,该对象定义了如何将分组后的结果收集到一个Map中。默认情况下,它会收集每个分组的元素并返回一个Map,其中键是分类函数的返回值,值是对应的元素列表。

@Test
public void test29(){
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19));
    Map<String, List<Student>> map = list.stream().collect(Collectors.groupingBy(item -> item.getName()));
    System.out.println(map.toString());
}

Collectors#groupingByConcurrent

        Collectors#groupingByConcurrent方法与Collectors#groupingBy方法功能类似,唯一有些区别就是,Collectors#groupingByConcurrent方法返回值是ConcurrentMap

@Test
public void test30(){
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19));
    ConcurrentMap<String, List<Student>> concurrentMap = list.stream().collect(Collectors.groupingByConcurrent(item -> item.getName()));
    System.out.println(concurrentMap.toString());
}

Collectors#partitioningBy

        Collectors.partitioningBy()用于将Stream中的元素进行分区操作。该方法的功能作用是根据给定的二元函数将Stream中的元素分成两个部分,并返回一个包含两个子列表的Map。

示例

根据姓名是否以z开头对姓名集合进行分区

@Test
public void test31(){
    Stream<String> stream = Stream.of("zhangsan", "lisi", "wangwu", "zhaoliu");
    Map<Boolean, List<String>> map = stream.collect(Collectors.partitioningBy(item -> item.startsWith("z")));
    System.out.println(map.toString());//输出结果:{false=[lisi, wangwu], true=[zhangsan, zhaoliu]}
}

Collectors#toMap

        Collectors.toMap()用于将Stream中的元素收集到Map中,它接受两个函数作为参数,一个用于映射键,另一个用于映射值。这些函数将应用于Stream中的每个元素,以生成对应的键和值。在收集元素时,如果存在重复的键,Collectors.toMap()方法的行为取决于是否提供了合并函数。如果提供了合并函数,该函数将用于处理重复键,以决定如何合并这些键的值。如果没有提供合并函数,那么在遇到重复键时,则会抛出IllegalStateException异常。

        示例

        把学生信息的List集合,转化为一个Map,key:学生姓名,value:学生年龄

@Test
public void test32(){
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19));
    Map<String, Integer> map = list.stream().collect(Collectors.toMap(item -> item.getName(), item -> item.getAge()));
    System.out.println(map.toString());//输出结果:{lisi=19, zhangsan=18}
    List<Student> list2 = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19),new Student("lisi", 20));
    Map<String, Integer> map2 = list2.stream().collect(Collectors.toMap(item -> item.getName(), item -> item.getAge(), (v1, v2) -> {
        return v2;//如果key发生重复,则取重复对象的最后一个对象的val
    }));
    System.out.println(map2);
}

Collectors#toUnmodifiableMap

        Collectors.toUnmodifiableMap()用于将Stream中的元素收集到一个不可修改的Map中。它接受一个二元函数作为参数,用于将Stream中的元素映射为Map中的键值对。返回的Map是不可修改的,即无法添加、删除或修改其中的元素。这样可以保证Map的安全性,防止意外修改原始数据。除了这些特性,在用法上与Collectors#toMap相同

Stream使用过程特别需要注意的几个坑

        在使用Java的Stream流时,有一些容易踩坑的地方需要注意:

  1. 空指针异常:在使用Stream时,需要注意避免空指针异常。例如,在使用filter方法过滤列表时,如果列表为空,会导致空指针异常。因此,在使用Stream之前,需要先判断数据是否为空,避免出现异常。
  2. 内存溢出:在使用Stream进行大数据处理时,需要注意内存溢出的问题。如果数据量太大,而内存不足,会导致内存溢出。因此,需要根据数据量和机器的内存情况合理选择数据分块大小和处理方式,避免出现内存溢出的问题。
  3. 并行流的正确使用:在使用并行流时,需要注意线程安全的问题。如果多个线程同时修改同一个数据,可能会导致数据不一致的问题。因此,在使用并行流时,需要确保数据不会被多个线程同时修改。
  4. 终止操作:在使用Stream时,需要注意终止操作。如果没有正确地终止Stream,可能会导致数据丢失或者出现其他异常。因此,在使用Stream时,需要确保每个操作都正确地终止。
  5. 链式操作的可读性:在使用链式操作时,需要注意可读性问题。如果链式操作太长或者操作太多,会导致代码难以理解和维护。因此,在使用链式操作时,需要合理控制操作的长度和数量,提高代码的可读性和可维护性。
  6. 类型转换问题:在使用Stream的map方法进行类型转换时,需要注意类型转换的正确性。例如,将一个字符串列表映射为对应的整数列表时,如果转换出错会导致类型转换异常。因此,在进行类型转换时,需要确保转换的正确性。
  7. 延迟加载问题:Stream的延迟加载问题是指在Stream操作完成之后,结果不会立即计算并返回,而是需要在使用结果时才会进行计算。这种延迟加载的特性可以减少内存占用和提高性能,但也可能导致一些问题。因此,在使用Stream时需要注意,需要通过正确地终止操作、缓存结果、使用并行流和使用正确的操作等方法来解决该问题。

Stream 的应用场景

        Java Stream的应用场景非常广泛,如果在实际的业务中以下类似的技术需求,那么都是可以使用Stream的相关能力助力业务实现:

  1. 数据筛选与过滤:Stream提供了filter方法,可以根据指定的条件筛选出符合要求的元素。在处理大量数据时,使用Stream可以避免手动编写循环和if语句的繁琐工作,使代码更加简洁易读。
  2. 映射操作:Stream的map方法可以将元素映射为其他对象。例如,可以将一个字符串列表映射为对应的字符串长度列表。
  3. 排序操作:Stream的sorted方法可以对元素进行排序。例如,可以将一个整数列表按照升序或降序排列。
  4. 聚合操作:Stream的reduce方法可以将元素进行聚合操作,例如将一个整数列表累加成一个整数。
  5. 收集操作:Stream的collect方法可以将元素收集到一个集合中。例如,可以将一个字符串列表收集到一个List中。
  6. 遍历操作:Stream的forEach方法可以遍历元素并执行指定的操作。例如,可以遍历一个整数列表并输出每个整数的值。

总结

        总之,Java Stream可以应用于各种数据处理的场景,包括但不限于数据筛选与过滤、映射操作、排序操作、聚合操作、收集操作和遍历操作等。使用Stream可以简化代码、提高可读性和可维护性,并提高系统的性能和效率。

下一篇:深入了解Java 8 新特性:Stream流的实践应用(一) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/172414.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么批量提取文件名字到Excel中?

怎么批量提取文件名字到Excel中&#xff1f;Excel是由微软公司开发的一种电子表格软件&#xff0c;它是Microsoft Office办公套件的一部分。Excel提供了强大的数据处理和分析功能&#xff0c;用户可以使用Excel创建、编辑和管理电子表格&#xff0c;进行各种计算、数据分析、图…

2024测试工程师必学的Jmeter:利用jmeter插件收集性能测试结果汇总报告和聚合报告

利用jmeter插件收集性能测试结果 汇总报告&#xff08;Summary Report &#xff09; 用来收集性能测试过程中的请求以及事务各项指标。通过监听器--汇总报告 可以添加该元件。界面如下图所示 汇总报告界面介绍&#xff1a; 所有数据写入一个文件&#xff1a;保存测试结果到本地…

通过AppLink把拼多多热门榜单商品同步至小红书

上篇说到AppLink当中定时调度方式如何配置&#xff0c;这次来演示一下&#xff0c;如何把热门榜单信息同步至小红书 1.拉取一个定时器作为触发动作&#xff0c;通过配置定时器调度时间将定时策略配置为每天执行一次 2.触发动作完成后通过好单库获取拼多多每日热门榜单&#xf…

steamui.dll找不到指定模块,要怎么修复steamui.dll文件

当我们使用Steam进行游戏时&#xff0c;有时可能会面对一些令人无奈的技术问题。一种常见的问题是“找不到指定模块steamui.dll”&#xff0c;这可能是由于缺少文件、文件损坏或软件冲突等原因导致。但别担心&#xff0c;这篇文章将提供几种解决此问题的方法&#xff0c;并针对…

“KeyarchOS:国产Linux新星的崛起与创新之路“

简介 KeyarchOS是一款由浪潮信息自主研发的服务器操作系统。它因为几个特点而受到我的青睐和一些用户的关注。 首先&#xff0c;KeyarchOS注重安全性和稳定性。它有一些防护和隔离功能&#xff0c;来帮助系统稳定运行&#xff0c;而且是中文语言更接地气。 其次&#xff0c;Ke…

基于Android校园交流uniAPP+vue 微信小程序v7e1

本系统结合现今XX校园交流APP的功能模块以及设计方式进行分析&#xff0c;使用Android平台和Ssm框架进行开发设计&#xff0c;具体研究内容如下&#xff1a; (1) 系统管理员主要对用户管理、类型管理、娱乐天地管理、投诉举报管理、学习平台、我的收藏管理、系统管理等功能进…

【设计模式】行为型设计模式

行为型设计模式 文章目录 行为型设计模式一、概述二、责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;三、命令模式&#xff08;Command Pattern&#xff09;四、解释器模式&#xff08;Interpreter Pattern&#xff09;五、迭代器模式&#xff08;Iterato…

蓝桥杯物联网_STM32L071_1_CubMxkeil5基础配置

CubMx配置&#xff1a; project工程中添加.h和.c文件&#xff1a; keil5配置: 运行&#xff1a; 代码提示与解决中文乱码&#xff1a;

想面试前端工程师,必须掌握哪些知识和技能?【云驻共创】

在当今的数字化时代&#xff0c;前端工程师扮演着至关重要的角色。他们负责设计和开发用户界面&#xff0c;使得用户能够与应用程序或网站进行互动。为了找到最出色的前端工程师&#xff0c;你需要了解哪些技能和知识是必备的&#xff0c;同时也要掌握一些面试技巧和常见的面试…

谷歌浏览器任意文件访问漏洞(CVE-2023-4357)复现

1.漏洞级别 高危 2.漏洞描述 该漏洞的存在是由于 Google Chrome中未充分验证 XML 中不受信任的输入。远程攻击者可利用该漏洞通过构建的 HTML 页面绕过文件访问限制&#xff0c;导致chrome任意文件读取。 总结&#xff1a;一个XXE漏洞 3.利用范围 Google Chrome < 116.…

亚马逊运营一定要用动/静态住宅IP代理吗?

作为全球最大的电商平台之一&#xff0c;亚马逊已经成为许多商家的首选销售平台。而代理IP作为近几天互联网的热门工具&#xff0c;在跨境电商界也起着非常强大的作用。那么在亚马逊运营中&#xff0c;适合动态住宅代理还是静态住宅代理呢&#xff1f;下面我们一起来探索&#…

普冉PY32系列(十) 基于PY32F002A的6+1通道遥控小车I - 综述篇

目录 普冉PY32系列(一) PY32F0系列32位Cortex M0 MCU简介普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单普冉PY32系列(四) PY32F002A/003/030的时钟设置普冉PY32系列(五) 使用JLink RTT代替串口输出日志普冉PY32…

初识shell脚本

目录 什么是shell脚本 面向过程语言 面向对象 windows 三种处理逻辑 常用的&#xff1a;usr/bin/bash 基本申明 三个基本语法 执行脚本 脚本错误 三种错误区别 set -e set -u 重定向 管道符 变量 常见的shell变量的类型包括 整数的运算 整数运算 $RANDOM 随机…

界面控件DevExpress WPF流程图组件,完美复制Visio UI!(一)

DevExpress WPF Diagram&#xff08;流程图&#xff09;控件帮助用户完美复制Microsoft Visio UI&#xff0c;并将信息丰富且组织良好的图表、流程图和组织图轻松合并到您的下一个WPF项目中。 P.S&#xff1a;DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至…

京东大数据(京东数据采集):2023年Q3线上投影仪品类销售数据分析报告

11月初&#xff0c;某知名投影仪企业发布了2023年三季度财报。数据显示&#xff0c;今年第三季度&#xff0c;公司营收依然不客观&#xff0c;连续第五个季度业绩持续下滑。 从鲸参谋数据也可以看出&#xff0c;今年Q3&#xff0c;京东平台上该品牌的销量环比下滑约35%&#x…

CyNix

CyNix 一、主机发现和端口扫描 主机发现&#xff0c;靶机地址192.168.80.146 arp-scan -l端口扫描&#xff0c;只开放了80和6688端口 nmap -A -p- -sV 192.168.80.146二、信息收集 访问80端口 路径扫描 gobuster dir -u http://192.168.80.146/ -w /usr/share/wordlists/dir…

GitHub 报告发布:TypeScript 取代 Java 成为第三受欢迎语言

GitHub发布的2023年度Octoverse开源状态报告发布&#xff0c;研究围绕AI、云和Git的开源活动如何改变开发人员体验&#xff0c;以及在开发者和企业中产生的影响。报告发现了三大趋势&#xff1a; 1、生成式AI的广泛应用&#xff1a; 开发人员大量使用生成式AI进行构建。越来越…

【Element】el-progress 自定义进度条

一、背景 要求弹窗内显示进度条&#xff0c;根据接口获取当前进度值&#xff0c;间隔5秒调用接口获取最新进度值&#xff0c;当进度值为100时&#xff0c;允许关闭进度条弹窗 二、效果 三、实现步骤 3.1、按钮绑定事件&#xff0c;打开弹窗 <el-button class"cance…

存储压测工具— — Cosbench教程

存储压测工具— — Cosbench教程 Cosbench是Intel团队基于java开发&#xff0c;对云存储的测试工具&#xff0c;全称是Cloud object Storage Bench&#xff0c;本文主要针对的是支持aws-s3协议的存储服务进行测试&#xff0c;包括seaweedfs、以及华为云存储。 1 安装 github地址…

[Docker]七.配置 Docker 网络

一.Docker0 网络 1.多个容器之间如何通信,是否可以直接连接 默认启动的所有容器都会加入到docker0这个网络中,所有各个容器件是可以直接通信的 先看看网卡信息: 启动几个容器来演示一下: #启动mycentos这个容器 [rootlocalhost zph]# docker run -it -d --name mycentos d757…