Java Stream 的使用

news2024/11/24 6:22:51

Java Stream 的使用

  • 开始
  • 中间操作
    • forEach 遍历
    • map 映射
    • flatMap 平铺
    • filter 过滤
    • limit 限制
    • sorted 排序
    • distinct 去重
  • 结束操作
    • collect 收集
      • toList、toSet 和 toMap
      • Collectors.groupingBy
      • Collectors.collectingAndThen
    • metch 匹配
    • find 查询
      • findFirst 与 findAny 的使用
      • Optional 的获取
    • count 计数
    • reduce 规约

Stream 的创建需要指定一个数据源,比如 java.util.Collection 的子类,任何集合类对象以及数组都可以作为这个数据源

它的作用就是链式的对一组元素进行操作,它的操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回 Stream 本身,以下是常见的操作
在这里插入图片描述
无状态:指元素的处理不受之前元素的影响
有状态:指该操作只有拿到所有元素之后才能继续下去
非短路操作:指必须处理所有元素才能得到最终结果
短路操作:指遇到某些符合条件的元素就可以得到最终结果

Stream 可以使用串行和并行来完成操作,串行操作是用一个线程依次执行,而并行操作使用了多线程,将 stream() 改为 parallelStream() 来使用并行流

流只能用计数器计数:流虽然可以实现很多高大上的操作,但是遇到需要使用数组下标的问题还是只能使用老土的计数器方式,因此如果程序中涉及到要取下标的操作还是推荐使用 for 循环

    int index = 0;
    list.stream().filter(s -> 
            //每比对一个元素,数值加1
            s.getId() == 10086 ? true : index++ == -1;
        ).findFirst();

流的操作大多需要使用 lambda 重写逻辑,它的常见操作如下:

开始

我们对一个集合或者迭代器使用 stream 方法就可以得到一个流了

   		Lists.newArrayList().stream();
   		Lists.newArrayList().parallelStream();

可以使用 java.util.Arrays.stream(T[] array) 方法用数组创建流

		int[] array={1,3,5,6,8};
		IntStream stream = Arrays.stream(array);

还可以使用 Stream 的 of 方法获取一个或者多个流

		Stream.of(splitter.splitToList(a, b));

同时,在 of 之后,使用 flatMap 方法即可将多个集合转换为一个流。使用 of 方法的话,各个数组并不是分别映射一个流,而是映射成多个流

		Stream.of(splitter.splitToList(a, b)).flatMap(Arrays::stream);

此时使用 flatMap(Array::stream) 可以将生成的多个流被合并起来,即扁平化为一个流

中间操作

forEach 遍历

该方法是最常见的,该方法用来迭代流中的每个数据,不只是 stream 对象,集合也可以直接使用 forEach 方法

		integers.forEach(System.out::println);

map 映射

该方法用于映射每个元素到对应的结果

		integers.stream().map(i -> i+1).forEach(System.out::println);

flatMap 平铺

map 是对流元素进行转换,flatMap 是对流中的元素(数组)进行平铺后合并,即对流中的每个元素平铺后又转换成为了 Stream 流

System.out.println("=====flatmap list=====");
List<String> mapList = list.stream().flatMap(Arrays::stream).collect(Collectors.toList());
mapList.forEach(System.out::print);
System.out.println("\nflatmap list size: " + mapList.size());
System.out.println();

一般在里面直接填入 Arrays::stream 即可

filter 过滤

该方法用于通过设置的条件过滤出元素,只有满足条件的(里面的方法返回为 true)才会留下来,其他的都会被过滤掉。该方法不会删除原集合的数据

		integers.stream().filter(a -> a/2 == 0).forEach(System.out::println);

limit 限制

该方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出前 10 条数据

	integers.limit(10).forEach(System.out::println);

sorted 排序

该方法用于对流用指定的操作进行排序,如果不传入 Comparator 排序器,则默认使用自然排序,自然排序需要流中元素需实现 Comparable 接口

	integers.sort((a1, a2) -> a2 - a1).forEach(System.out::println);

distinct 去重

方法用于去除流中的重复元素,该方法不用传入参数

	integers.distinct().forEach(System.out::println);

distinct 使用 hashCode 和 equals 方法来获取不同的元素。因此,我们的类必须实现 hashCode 和 equals 方法

结束操作

以上方法属于中间操作,返回 Stream 本身,结束操作不会指的是不返回 Stream 的方法

collect 收集

该方法可以返回列表或者字符串,该方法可以接收一个集合实例,将流中元素收集成该集合实例

但是 collect 的功能不止于此,它可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合

toList、toSet 和 toMap

collect 主要依赖 java.util.stream.Collectors 类内置的静态方法,下面用一个案例演示 toList、toSet 和 toMap,以及把一个集合配合一些分隔符链接为一个字符串

List<String> list = strings.stream().collect(Collectors.toList());

List<String> set = strings.stream().collect(Collectors.toList());

Map<String, String> map = strings.stream().collect(Collectors.toMap(k -> k, v -> v));

String mergedString = strings.stream().collect(Collectors.joining(", "));

我们在 Collectors.toMap 中对重复的 key 去重

Map<Integer, String> map = list.stream().collect(Collectors.toMap(Person::getId, Person::getName, (oldValue, newValue) -> newValue));

Function.identity() 这个函数代表输入什么值就输出什么值,相当与 v -> v

后面的参数则代表遇到重复的 key 时在老值与新值之中取哪个值,举个例子:

list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (oldV, newV) -> oldV)).values().stream();

你还可以根据这个特性将重复的 key,放入 List,(当然我们也可以通过 groupBy 完成这件事)

Map<String, List<Working>> map =
                workings.stream().collect(Collectors.toMap(Working::getInvoicePage,
                        e -> {
                            ArrayList<Working> list = new ArrayList<>();
                            list.add(e);
                            return list;
                        },
                        (oldList, newList) -> {
                            oldList.addAll(newList);
                            return oldList;
                        }));

Collectors.groupingBy

举个例子,我们可以优雅的对某个集合做分组统计,比如在学生这个 pojo 中,对属性班级做分组或者做分组统计

Map<Integer, List<Student>> studentGroup = studentList.stream().collect(Collectors.groupingBy(Student::getClassNumber));

Map<Integer, Long> map = studentList.stream().collect(Collectors.groupingBy(Student::getClassNumber, Collectors.counting()));

上面例子中的第二个,我们修改了返回 Map 值的类型。第二个重载 groupingBy 方法带另一个参数指定后续收集器,应用于第一个集合结果。当我们仅指定一个分类器函数,没有后续收集器,则返回 toList() 集合。如何后续收集器使用 toSet(),则会获得 Set 集合,而不是 List。我们甚至可以对已经分组的集合再进行分组

Map<BlogPostType, Set<BlogPost>> postsPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType, toSet()));
  
Map<String, Map<BlogPostType, List>> map = posts.stream()
  .collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType)));

三个参数的 groupingBy 方法,通过提供 Map supplier 函数,其允许我们改变 Map 的类型

EnumMap<BlogPostType, List<BlogPost>> postsPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType, 
  () -> new EnumMap<>(BlogPostType.class), toList()));

Collectors.collectingAndThen

Collectors.collectingAndThen() 函数应该最像 map and reduce 了,它可接受两个参数,第一个参数用于 reduce 操作,而第二参数用于 map 操作

先把流中的所有元素传递给第一个参数,然后把生成的集合传递给第二个参数来处理

例如下面的代码

  • 把 [1,2,3,4] 这个集合传递给 v -> v * 2 lambda表达式,计算得出结果为[2,4,6,8]
  • 然后再把 [2,4,6,8]传递给 Collectors.averagingLong 表达式,计算得出 5.0
  • 然后传递给 s -> s * s lambda表达式,计算得到结果为 25.0
@Test
public void collectingAndThenExample() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4);
    Double result = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingLong(v -> {
                System.out.println("v--" + v + "--> " + v * 2);
                return v * 2;
            }),
            s -> {
                System.out.println("s--" + s + "--> " + s * s);
                return s * s;
            }));
    System.out.println(result);
}

再比如,我想用对象中的某个属性 list 去重,就可以这么写

                Stream.of(wechatUserInfosFromBackend, wechatUserInfosFromC2b)
                        .flatMap(Collection::stream)
                        .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<WechatUserInfo>(Comparator.comparing(WechatUserInfo::getExternalUserid))), ArrayList::new))

metch 匹配

metch 函数只返回 true 与 false,该方法会对传入的数据进行逐个判断,有以下几种类型

  • allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回 true,否则返回 false
  • noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回 true,否则返回 false
  • anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回 true,否则返回 false

find 查询

有以下两个分支

  • findFirst 返回流中满足条件的第一个元素
  • findAny:返回流中找到的第一个元素

这两个方法在串行流中的概念以及效果是一模一样的,不同之处在于并行流

findFirst 与 findAny 的使用

在并行流中的 findAny() 操作,返回的元素是不确定的,对于同一个列表多次调用 findAny() 有可能会返回不同的值。使用 findAny() 是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个

举个栗子:

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list.parallelStream().filter(a -> a.equals(2) || a.equals(1) || a.equals(3)).findAny().get());
        System.out.println(list.parallelStream().filter(a -> a.equals(2) || a.equals(1) || a.equals(3)).findFirst().get());

这里两个并行流的结果是不一样的,findAny 方法会侧重第一个查到返回值的线程,而 findFirst 则会侧重数组中第一个满足条件的值

Optional 的获取

Optional 是对 stream 使用 findFirst 或者 findAny 方法会得到的类,它是为了防止空指针问题而被创造出来的,本来这块内容不应该是流结束操作涉及到的,但是 findAny 或者 findAny 也算是和 stream 结束有关系

我们在获取 Optional 后直接 get 会提示没有进行赋值检查,因此不推荐直接 get,准确的写法如下:

     Optional.of("has value").orElse(getDefault());
     Optional.of("has value").orElseGet(() -> getDefault());
     Optional.of("has value").ifPresent(A::setB);

说一下 orElse 与 orElseGet 的区别,orElse 是传值的,所以里面的表达式会立即执行(在传入一个方法的时候),如果 optional 有值也会执行就没必要了;而 orElseGet 接受的是一个 function,只有 optional 为空的时候才会被执行,因此不会让 cpu 资源被浪费

尽量在 orElse 中传入属性,在 orElseGet 中传入方法,如果在 orElse 中传入了方法,而且方法中含有更新修改类的操作,这样就不光是 CPU 或者耗时的问题了

count 计数

返回流中元素的总个数

	strings.stream().count();

reduce 规约

规约操作(reduction operation)又被称作折叠操作(fold),是通过某个连接动作将所有元素汇总成一个汇总结果的过程。元素求和、求最大值或最小值、求出元素总个数、合并、将所有元素转换成一个元素,都属于规约操作

reduce 擅长的是生成一个值,而 collect 擅长从 Stream 中生成一个集合或者 Map 等复杂的对象

reduce 的方法定义有三种重写形式,我们需要按顺序定义以下三种模式:

  • Identity : 定义一个元素代表是归并操作的初始值,如果Stream 是空的,也是 Stream 的默认结果
  • Accumulator: 定义一个带两个参数的函数,第一个参数是上个归并函数的返回值,第二个是Strem 中下一个元素。
  • Combiner: 调用一个函数来组合归并操作的结果,当归并是并行执行或者当累加器的函数和累加器的实现类型不匹配时才会调用此函数

举几个例子,我们可以用它找出数组中最有特色的值,比如最大值最小值:

    Optional<String> res = Stream.of("zhangsan", "lisi", "wanger", "mazi")
            .reduce((s1, s2) -> s1.length() >= s2.length() ? s1 : s2);
    System.out.println(res.get()); 

    Optional<String> res2 = Stream.of("zhangsan", "lisi", "wanger", "mazi")
            .max((s1, s2) -> s1.length() - s2.length());
    System.out.println(res2.get()); 

可以用它来求和

        Integer res1 = Arrays.stream(new Integer[]{2, 4, 6, 8})
                .reduce(0, Integer::sum);

很明显我们只能将列表变成列表中的一个元素,那假如需要将整数列表变成一个字符串,我们该如何操作呢?我们当然可以先使用 map 做转换,但是也可以使用 reduce 提供的第三个参数

int result = users.stream()
  .reduce("", (a, b) -> a + String.valueOf(b), String::concat);

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

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

相关文章

下载免费商用字体,就上这6个网站。

我不允许还有人不知道&#xff0c;这些可以免费下载商用字体的网站&#xff0c;必须收藏好了&#xff0c;有了这6个网站&#xff0c;再也不用担心字体侵权了。 1、字体搬运工 https://font.sucai999.com/ 一个免费可商用字体搬运工&#xff0c;实时跟新市面上免费商用的字体。…

No204.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

java程序中为什么经常使用tomcat

该疑问的产生场景&#xff1a; 原来接触的ssm项目需要在项目配置中设置tomcat&#xff0c;至于为什么要设置tomcat不清楚&#xff0c;只了解需要配置tomcat后项目才能启动。接触的springboot在项目配置中不需要配置tomcat&#xff0c;原因是springboot框架内置了tomcat&#xf…

1、 图像和像素

像素我们不陌生,图像我们更不陌生。 学习计算机视觉,我觉得第一步就是要了解我们要处理的对象,就像上一篇说到的,计算机视觉任务中,图像(像素)是原材料,算法是菜谱。 了解了图像的特征,才可以更好的完成更多图像处理任务,比如对一张图片进行分类,或者对一张图片画…

CNN进展:AlexNet、VGGNet、ResNet 和 Inception

一、说明 对于初学者来说&#xff0c;神经网络进展的历程有无概念&#xff1f;该文综合叙述了深度神经网络的革命性突破&#xff0c;从AlexNet开始&#xff0c;然后深度VGG的改进&#xff0c;然后是残差网络ResNet和 Inception&#xff0c;如果能讲出各种特色改进点的和改进理由…

【Python】【应用】Python应用之一行命令搭建http、ftp服务器

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;Python应用&…

MQ四大消费问题一锅端:消息不丢失 + 消息积压 + 重复消费 + 消费顺序性

RabbitMQ-如何保证消息不丢失 生产者把消息发送到 RabbitMQ Server 的过程中丢失 从生产者发送消息的角度来说&#xff0c;RabbitMQ 提供了一个 Confirm&#xff08;消息确认&#xff09;机制&#xff0c;生产者发送消息到 Server 端以后&#xff0c;如果消息处理成功&#xff…

【python 生成器 面试必备】yield关键字,协程必知必会系列文章--自己控制程序调度,体验做上帝的感觉 2

这篇文章要解决的问题&#xff1a;How to Pass Value to Generators Using the “yield” Expression in Python ref:https://python.plainenglish.io/yield-python-part-ii-e93abb619a16 1.如何传值 yield 是一个表达式&#xff01;&#xff01;&#xff01;&#xff01; yi…

WebGl-Blender:建模 / 想象成形 / 初识 Blender

一、理解Blender 欢迎来到Blender&#xff01;Blender是一款免费开源的3D创作套件。 使用Blender&#xff0c;您可以创建3D可视化效果&#xff0c;例如建模、静态图像&#xff0c;3D动画&#xff0c;VFX&#xff08;视觉特效&#xff09;快照和视频编辑。它非常适合那些受益于…

【Python图像超分】Real-ESRGAN图像超分模型(超分辨率重建)详细安装和使用教程

1 前言 图像超分是一种图像处理技术&#xff0c;旨在提高图像的分辨率&#xff0c;使其具有更高的清晰度和细节。这一技术通常用于图像重建、图像恢复、图像增强等领域&#xff0c;可以帮助我们更好地理解和利用图像信息。图像超分技术可以通过多种方法实现&#xff0c;包括插值…

【每日一题】阈值距离内邻居最少的城市

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;多源最短路 写在最后 Tag 【多源最短路】【数组】【2023-11-14】 题目来源 1334. 阈值距离内邻居最少的城市 题目解读 题目翻译过来是这样的&#xff1a;一共 n 个城市&#xff0c;统计在每个城市 dt 距离范围内所有…

【java学习—十四】反射获取类的父类、接口、构造方法、方法(3)

文章目录 1. 通过反射获取一个类的父类和接口2. 反射获取一个类的构造方法3. 反射获取全部构造器4. 通过反射创建一个对象5. 反射机制获取类的方法 1. 通过反射获取一个类的父类和接口 使用反射可以取得&#xff1a; 实现的全部接口 public Class<?>[] getInterfaces(…

Python实现扫雷游戏,代码示例,边玩边学+回忆童年!

文章目录 前言实现总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前言 扫雷是一款益智类小游戏&#xff0…

香港科技大学广州|智能制造学域机器人与自主系统学域博士招生宣讲会—中国科学技术大学专场

&#x1f3e0;地点&#xff1a;中国科学技术大学西区学生活动中心&#xff08;一楼&#xff09;报告厅 【宣讲会专场1】让制造更高效、更智能、更可持续—智能制造学域 &#x1f559;时间&#xff1a;2023年11月16日&#xff08;星期四&#xff09;18:00 报名链接&#xff1a…

百望云携手华为发布金融信创与数电乐企联合方案 创新金融合规变革

10月27日&#xff0c;北京发布《关于开展全面数字化的电子发票试点工作的公告》&#xff0c;自2023年11月01日起开展数电票试点。千呼万唤始出来&#xff0c;拉开了北京地区企业开展数电票试点的序幕。 百望云作为数电票行业翘楚&#xff0c;电子发票服务平台供应商&#xff0c…

【C/C++底层】内存分配:栈区(Stack)与堆区(Heap)

/*** poject * author jUicE_g2R(qq:3406291309)* file 底层内存分配&#xff1a;栈区(Stack)与堆区(Heap)* * language C/C* EDA Base on MVS2022* editor Obsidian&#xff08;黑曜石笔记软件&#xff09;* * copyright 2023* COPYRIGHT …

论文阅读:Robust High-Resolution Video Matting with Temporal Guidance

发表时间&#xff1a;2021年8月25日 项目地址&#xff1a;https://peterl1n.github.io/RobustVideoMatting/ 论文地址&#xff1a;https://arxiv.org/pdf/2108.11515.pdf 我们介绍了一种鲁棒的&#xff0c;实时的&#xff0c;高分辨率的人体视频匹配方法&#xff0c;以实现了新…

【C++】:STL——标准模板库介绍 || string类

&#x1f4da;1.什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且 是一个包罗数据结构与算法的软件框架 &#x1f4da;2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在…

得帆信息携手深信服,联合打造高安全PaaS超融合一体化解决方案

上海得帆信息技术有限公司&#xff08;以下简称“得帆”&#xff09;和深信服科技股份有限公司&#xff08;以下简称“深信服”&#xff09;携手推出融合安全性、稳定性、高效性于一体的全新PaaS超融合解决方案。 用户痛点分析 全面推进企业数字化与信息化的趋势下&#xff0c;…

伊朗黑客对以色列科技行业发起恶意软件攻击

最近&#xff0c;安全研究人员发现了一场由“Imperial Kitten”发起的新攻击活动&#xff0c;目标是运输、物流和科技公司。 “Imperial Kitten”又被称为“Tortoiseshell”、“TA456”、“Crimson Sandstorm”和“Yellow Liderc”&#xff0c;多年来一直使用“Marcella Flore…