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

news2024/11/16 15:48:33

阅读建议

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

  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流的实践应用(一) 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML5+CSS3+JS小实例:Canvas图片滑块拖动验证码

实例:Canvas图片滑块拖动验证码 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" cont…

二十一、数组(3)

本章概要 Arrays的setAll方法增量生成 Arrays的setAll方法 在Java 8中&#xff0c; 在RaggedArray.java 中引入并在 ArrayOfGenerics.java.Array.setAll() 中重用。它使用一个生成器并生成不同的值&#xff0c;可以选择基于数组的索引元素&#xff08;通过访问当前索引&…

Android Serializable / Parcelable

Serializable 序列化,将对象转为二进制序列 Parcelable 不是序列化,属于进程间通信,不需要IO/操作,没有拷贝内存的操作, Object -> ShareMemory -> Object 不需要IO,使用内存共享等方式 Kotlin inline fun 内联函数 TCP协议将数据包拆分,进行发送,保证网络数据的可…

echarts折线图修改特定点的颜色

$.ajax({url:"/plc1672Ctrl/selectPage2.ctrl",dataType:"json",type:"POST",cache:false,data:{"serNo":$("#search").val().trim()},success:function(data){var list data.list;// x坐标var x new Array();// y坐标var…

ssm租房小程序-计算机毕设 附源码42196

SSM租房小程序 摘 要 本论文主要论述了如何使用SSM框架开发一个租房小程序&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构JAVA技术&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述租房小程序的当前背景以及系…

面试题:你怎么理解System.out.println() ?

文章目录 首先分析System源码out源码分析println分析拓展知识点 你如何理解System.out.println() ? 学了这么久的面向对象编程&#xff0c;那如何用一行代码体现呢&#xff1f; 如果你能自己读懂System.out.println()&#xff0c;就真正了解了Java面向对象编程的含义 面向对…

低代码服务商,中小型数字化软件服务商的新出路

数字化时代大背景下&#xff0c;企业信息化向数字化转型成为所有企业发展的必由之路&#xff0c;企业在对业务模式、流程、组织形式、信息技术等方面进行重新定义时&#xff0c;软件必然参与价值创造的全过程&#xff0c;这势必驱使软件成为推动数字化转型的“引擎”&#xff0…

debian 12 配置

1. 修改apt源 修改apt源为http版本 # 默认注释了源码镜像以提高 apt update 速度&#xff0c;如有需要可自行取消注释 deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware # deb-src http://mirrors.tuna.tsinghua.edu.cn/d…

半导体业库存问题缓解,明年迎来良好转机 | 百能云芯

随着全球半导体产业今年产值预计将出现逾1成的衰退&#xff0c;市场一度陷入不确定性。然而&#xff0c;半导体厂商们对于供应链库存的有效去化表示乐观&#xff0c;预计将为明年带来健康的复苏。在各种因素交织的复杂情况下&#xff0c;半导体产业展现出逐步解决库存问题、迎来…

阿里云oss文件上传入门

先登录&#xff1a; 点击进入控制台 点击左上角导航栏按钮 搜索oss&#xff0c;点击进入 进入之后点击立即开通oss按钮&#xff0c;开通之后点击下图立即创建&#xff0c;弹出创建Bucket 填上Bucket名称&#xff0c;读写权限改为公共读。其他不变点击确定创建&#xff0c;完成…

Motion Plan之搜索算法笔记

背景&#xff1a; 16-18年做过一阵子无人驾驶&#xff0c;那时候痴迷于移动规划&#xff1b;然而当时可学习的资料非常少&#xff0c;网上的论文也不算太多。基本就是Darpa的几十篇无人越野几次比赛的文章&#xff0c;基本没有成系统的文章和代码讲解实现。所以对移动规划的认…

【深度学习实验】注意力机制(三):打分函数——加性注意力模型

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 理论介绍a. 认知神经学中的注意力b. 注意力机制 1. 注意力权重矩阵可视化&#xff08;矩阵热图&#xff09;2. 掩码Softmax 操作3. 打分函数——加性注意力模型1. 初始化2. 前向传播3. 内部组件…

SQL零基础入门教程,贼拉详细!贼拉简单! 速通数据库期末考!(十一)

COUNT() 计数函数 COUNT() 函数返回匹配指定条件的行数。 语法&#xff1a; 1.返回指定列的字段值条数 SELECT COUNT(column_name) FROM table_name;2.返回整表数据行条数 SELECT COUNT(*) FROM table_name;3.返回指定列去重后的字段值条数 SELECT COUNT(DISTINCT column_…

猫罐头牌子哪个好一点?精选5款口碑好的猫罐头推荐!

猫罐头牌子哪个好一点&#xff1f;选择猫罐头是十分重要的事情&#xff0c;千万不能将就。因为&#xff0c;好的猫罐头不仅可以营养丰富&#xff0c;水分充足&#xff0c;适口性好&#xff0c;还能易吸收。而一旦选择错误&#xff0c;不仅无法达到上述效果&#xff0c;还可能产…

服务号迁移到订阅号流程步骤

服务号和订阅号有什么区别&#xff1f;服务号转为订阅号有哪些作用&#xff1f;首先我们要知道服务号和订阅号有什么区别。服务号侧重于对用户进行服务&#xff0c;每月可推送4次&#xff0c;每次最多8篇文章&#xff0c;发送的消息直接显示在好友列表中。订阅号更侧重于信息传…

Stable Diffusion专场公开课

从SD原理、本地部署到其二次开发 分享时间&#xff1a;11月25日14&#xff1a;00-17&#xff1a;00 分享大纲 从扩散模型DDPM起步理解SD背后原理 SD的本地部署:在自己电脑上快速搭建、快速出图如何基于SD快速做二次开发(以七月的AIGC模特生成系统为例) 分享人简介 July&#…

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

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

循环链表3

插入函数——插入数据&#xff0c;在链表plsit的pos位置插入val数据元素 位置pos&#xff08;在无特别说明的情况下&#xff09;是从0开始计数的 要改变链表结构&#xff0c;就要依赖前驱&#xff0c;每个前驱的next存储着下一个数据结点的地址&#xff0c;也就是依靠前驱的ne…

笔尖笔帽检测3:Android实现笔尖笔帽检测算法(含源码 可是实时检测)

目录 1. 前言 2.笔尖笔帽检测方法 (1)Top-Down(自上而下)方法 (2)Bottom-Up(自下而上)方法&#xff1a; 3.笔尖笔帽关键点检测模型训练 4.笔尖笔帽关键点检测模型Android部署 &#xff08;1&#xff09; 将Pytorch模型转换ONNX模型 &#xff08;2&#xff09; 将ONNX模…

leetcode刷题日记:205. Isomorphic Strings(同构字符串)

205. Isomorphic Strings(同构字符串) 对于同构字符串来说也就是对于字符串s与字符串t&#xff0c;对于 s [ i ] s[i] s[i]可以映射到 t [ i ] t[i] t[i],同时对于任意 s [ k ] s [ i ] s[k]s[i] s[k]s[i]都有 s [ k ] s[k] s[k]映射到 t [ k ] t[k] t[k],则 t [ k ] t [ i …