Java Stream API的二度深入

news2024/11/18 7:47:07

Java Stream API的二度深入

在这里插入图片描述

前言

为什么会写这样一篇文章呢?

1.面试的时候,一位前辈对我这方面有过一次提问,我随口回答,前辈很信任我,以此文致敬前辈!

2.去回顾,去扎实,对得起前辈的信任!

3.Stream API 用起来很方便,所以很重要,所以还得学!

Stream API的三个阶段

在Java中,Stream 是Java 8引入的一个新概念,用于处理集合(Collections)数据的一种抽象。Java的Stream API 提供了一种声明式的方式来操作数据集合,可以用更简洁、可读性更强的代码来进行集合的操作。

Java Stream API的操作可以分为三个阶段:

  1. 创建流(Creation of Stream): 这个阶段涉及到从不同的数据源创建流,可以是集合、数组、I/O通道等。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> stream = numbers.stream();
    
  2. 中间操作(Intermediate Operations): 这个阶段包括对流的转换操作,可以对流进行过滤、映射、排序等操作。这些操作并不会改变原始数据源,而是返回一个新的流。

    Stream<Integer> filteredStream = stream.filter(x -> x > 2);
    
  3. 终端操作(Terminal Operations): 这个阶段是对流进行最终操作,触发流的遍历,可以产生一个结果或者副作用。终端操作是流的最后一个操作,执行后流将不可再用。

    long count = filteredStream.count();
    

这三个阶段的设计使得可以通过链式调用的方式组合多个操作,从而编写更为清晰和简洁的代码。这种方式也有助于提高代码的可读性和可维护性。

当然,这里只是对于Stream API三个阶段的概述,只是告诉大家,简单分为三个阶段,至于三个阶段里面有哪些主要的方法,我们在下文进行详细叙述,这里我们点到为止!现在,大家心里面就应该有这么一个蓝图,或者是基本框架,知道我们接下来将会沿着那个几个方向展开叙述!

创建Stream

在Java中,你可以使用多种方式来创建Stream流。

  1. 从集合创建:

    使用集合类的 stream()parallelStream() 方法可以创建对应的流。例如:

    List<String> list = Arrays.asList("apple", "banana", "orange");
    Stream<String> streamFromList = list.stream();
    
  2. 从数组创建:

    使用 Arrays.stream() 方法可以从数组中创建流:

    String[] array = {"apple", "banana", "orange"};
    Stream<String> streamFromArray = Arrays.stream(array);
    
  3. 通过Stream的静态方法创建:

    Stream 类提供了静态方法 of(),可以传入一系列元素来创建流:

    Stream<String> stream = Stream.of("apple", "banana", "orange");
    
  4. 使用Stream的generateiterate方法:

    Stream 类还提供了 generateiterate 方法,用于生成无限流:

    // 生成包含随机整数的无限流
    Stream<Integer> infiniteStream = Stream.generate(() -> (int) (Math.random() * 100));
    
    // 从指定的起始值开始,按照某个规则生成无限流
    Stream<Integer> sequentialStream = Stream.iterate(1, n -> n + 1);
    
  5. 通过文件生成流:

    java.nio.file.Files 类提供了静态方法 lines(),可以用来读取文件内容并生成流:

    Path path = Paths.get("example.txt");
    Stream<String> fileLines = Files.lines(path);
    
  6. 使用正则表达式生成流:

    Pattern 类的 splitAsStream 方法可以根据正则表达式将字符串分割成流:

    String text = "apple,orange,banana";
    Stream<String> textStream = Pattern.compile(",").splitAsStream(text);
    

Stream API中间操作

Stream API 提供了许多中间操作,用于对流进行转换、筛选和处理。

  1. filter

    用于筛选元素,根据指定的条件保留符合条件的元素。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> filteredStream = numbers.stream().filter(x -> x > 2);
    
  2. map

    对流中的每个元素应用指定的函数,并将结果映射为一个新的元素。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    Stream<Integer> wordLengths = words.stream().map(String::length);
    
  3. flatMap

    将流中的每个元素都转换为一个流,然后将这些流连接起来成为一个流。

    List<List<Integer>> numbers = Arrays.asList(
        Arrays.asList(1, 2),
        Arrays.asList(3, 4),
        Arrays.asList(5, 6)
    );
    
    Stream<Integer> flatStream = numbers.stream().flatMap(List::stream);
    
  4. distinct

    去除流中的重复元素。

    List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
    Stream<Integer> distinctNumbers = numbers.stream().distinct();
    
  5. sorted

    对流中的元素进行排序。

    List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
    Stream<Integer> sortedNumbers = numbers.stream().sorted();
    
  6. peek

    对流中的每个元素执行操作,主要用于调试和观察流中的元素。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    Stream<String> peekStream = words.stream().peek(System.out::println);
    
  7. limitskip

    limit 用于截断流,保留指定数量的元素,而 skip 则用于跳过指定数量的元素。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> limitedStream = numbers.stream().limit(3);
    Stream<Integer> skippedStream = numbers.stream().skip(2);
    
  8. takeWhiledropWhile(Java 9+):

    takeWhile 返回元素满足指定条件的最长前缀,dropWhile 返回删除满足指定条件的最长前缀后的流。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> takenWhileStream = numbers.stream().takeWhile(x -> x < 4);
    Stream<Integer> droppedWhileStream = numbers.stream().dropWhile(x -> x < 4);
    

Stream API终端操作

Stream API 的终端操作用于触发对流的最终操作,产生结果或者引起副作用。

  1. forEach

    对流中的每个元素执行指定的操作。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    words.stream().forEach(System.out::println);
    
  2. toArray

    将流中的元素转换为数组。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    String[] wordArray = words.stream().toArray(String[]::new);
    
  3. reduce

    对流中的元素进行归约操作,可以用于求和、求最大值、最小值等。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Optional<Integer> sum = numbers.stream().reduce(Integer::sum);
    
  4. collect

    将流中的元素收集到一个集合中,例如 List、Set 或 Map。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    List<String> collectedWords = words.stream().collect(Collectors.toList());
    
  5. count

    返回流中的元素数量。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    long count = numbers.stream().count();
    
  6. anyMatchallMatchnoneMatch

    用于检查流中是否存在满足指定条件的元素。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    boolean anyGreaterThanThree = numbers.stream().anyMatch(x -> x > 3);
    boolean allGreaterThanTwo = numbers.stream().allMatch(x -> x > 2);
    boolean noneGreaterThanFive = numbers.stream().noneMatch(x -> x > 5);
    
  7. findAnyfindFirst

    返回流中的任意一个元素或者第一个元素。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    Optional<String> anyWord = words.stream().findAny();
    Optional<String> firstWord = words.stream().findFirst();
    
  8. minmax

    返回流中的最小值或最大值。

    List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
    Optional<Integer> minNumber = numbers.stream().min(Integer::compare);
    Optional<Integer> maxNumber = numbers.stream().max(Integer::compare);
    

注意事项

使用Stream API时,有一些需要注意的重要事项,以确保正确、高效地利用这一功能:

  1. 只能使用一次: 一个 Stream 实例只能被消费(执行终端操作)一次。如果你尝试对已经使用过的流进行其他终端操作,会抛出 IllegalStateException 异常。如果需要再次操作,可以重新创建一个新的流。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    Stream<String> wordStream = words.stream();
    
    // 正确的做法
    long count = wordStream.count();
    
    // 错误的做法,会抛出IllegalStateException
    long anotherCount = wordStream.count();
    
  2. 及早退出: 在处理大量数据时,及早退出可以提高性能。使用 anyMatch()findFirst() 等终端操作时,一旦找到符合条件的元素,就会立即返回,不再继续处理后续元素。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    boolean anyGreaterThanThree = numbers.stream().anyMatch(x -> {
        System.out.println("Checking: " + x);
        return x > 3;
    });
    
  3. 并行流的谨慎使用: Stream API 提供了并行流的支持,可以通过 parallel() 方法将顺序流转换为并行流。但并不是所有的场景都适合使用并行流,因为在某些情况下,并行流可能会导致性能下降,甚至出现并发问题。在并行流的使用上需要注意线程安全等问题。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    long count = numbers.parallelStream().filter(x -> x > 2).count();
    
  4. 避免可变状态: 在使用 map()flatMap() 等操作时,避免修改流中的元素或者使用可变状态。这有助于确保流操作的无状态性,避免副作用。

    // 避免修改流中的元素
    List<String> words = Arrays.asList("apple", "banana", "orange");
    words.stream().map(String::toUpperCase).forEach(System.out::println);
    
    // 避免使用可变状态
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.stream().reduce(0, Integer::sum);
    
  5. 了解操作的性能特性: 不同的操作对性能的影响是不同的。例如,limit() 对顺序流的性能影响较小,但对并行流的性能影响较大。在选择操作时,了解其性能特性对于优化代码是有帮助的。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> limitedStream = numbers.stream().limit(3);  // 对顺序流影响较小,但对并行流影响较大
    
  6. 使用适当的数据结构: 在创建流时,选择适当的数据结构能够影响流操作的性能。例如,ArrayList 在顺序访问时性能较好,而 LinkedList 在随机访问时性能较好。

    List<String> words = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));
    Stream<String> wordStream = words.stream();
    

总体而言,了解Stream API的使用原则,结合具体的业务场景和性能需求,能够更好地利用Stream API完成任务。注意流的延迟计算特性,避免副作用,可以使代码更加清晰、可读,并提高代码的可维护性。

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

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

相关文章

Spring及工厂模式概述

文章目录 Spring 身世什么是 Spring什么是设计模式工厂设计模式什么是工厂设计模式简单的工厂设计模式通用的工厂设计 总结 在 Spring 框架出现之前&#xff0c;Java 开发者使用的主要是传统的 Java EE&#xff08;Java Enterprise Edition&#xff09;平台。Java EE 是一套用于…

SwiftUI 支持拖放功能的集合视图(Grid)如何捕获手指按下并抬起这一操作

功能需求 假设我们开发了一款 SwiftUI 应用,其中用户可以通过拖放 Grid 中的 Cell 来完成一些操作。现在,我们希望用户在某个 Cell 被按下并随后抬起手指时得到通知,这能够实现吗? 如上图所示,我们准确地捕获到了手指在 Grid 的 Cell 上按下再抬起这一操作!那么它是如何…

算法沉淀——穷举、暴搜、深搜、回溯、剪枝综合练习四(leetcode真题剖析)

算法沉淀——穷举、暴搜、深搜、回溯、剪枝综合练习四 01.解数独02.单词搜索03.黄金矿工04.不同路径 III 01.解数独 题目链接&#xff1a;https://leetcode.cn/problems/sudoku-solver/ 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&am…

亿道丨三防平板丨加固平板丨为零售业提供四大优势

随着全球经济的快速发展&#xff0c;作为传统行业的零售业也迎来了绝佳的发展机遇&#xff0c;在互联网智能化的大环境下&#xff0c;越来越多的零售企业选择三防平板电脑作为工作中的电子设备。作为一种耐用的移动选项&#xff0c;三防平板带来的不仅仅是坚固的外壳。坚固耐用…

第十四章[面向对象]:14.9:定制类

一,__len__()方法返回长度 1,len()函数 len()函数: 功能:len() 函数返回对象(字符、列表、元组等)长度或项目个数 语法: len( s ) 参数:s : 要查询长度的对象 返回值: 返回对象长度 2,没有定义__len__()方法时,对实例应用len()函数会引发TypeError class Student: …

T-Dongle-S3开发笔记——分区表

参考&#xff1a; ESP32之 ESP-IDF 教学&#xff08;十三&#xff09;—— 分区表_esp32分区表-CSDN博客 分区表 - ESP32 - — ESP-IDF 编程指南 latest 文档 (espressif.com) 分区表是 ESP32 划分内部 flash 闪存的清单&#xff0c;它将 flash 划分为多个不同功能的区域用于…

【Java程序设计】【C00296】基于Springboot的4S车辆管理系统(有论文)

基于Springboot的4S车辆管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的4S店车辆管理系统 本系统分为销售员功能模块、管理员功能模块以及维修员功能模块。 管理员功能模块&#xff1a;管理员登录进入4S…

ubantu设置mysql开机启动

阅读本文之前请参阅----MySQL 数据库安装教程详解&#xff08;linux系统和windows系统&#xff09; 在Ubuntu系统中设置MySQL开机启动&#xff0c;通常有以下几种方法&#xff1a; 1. **使用systemctl命令**&#xff1a; Ubuntu 16.04及更高版本使用systemd作为…

【若依(ruoyi)】Java---如何在Apifox上传params参数--延伸--如何在Apifox上传Map类型参数

在使用若依开发过程中写接口的时候想在params中添加参数,但是使用params.key这种形式在后端是接收不到传过来的参数的,于是百般调研(百度),终于找到一个解决办法,就是在参数前后加上%5B和%5D,这两个参数会被编译为"["和"]",于是就对得上了,后端成功接受到参…

设计模式: 策略模式

文章目录 一、什么是策略模式二、策略模式结构三、使用场景案例分析1、使用场景2、案例分析&#xff08;1&#xff09;消除条件分支 一、什么是策略模式 策略模式是一种行为型设计模式&#xff0c;它允许定义一组算法&#xff0c;并将每个算法封装在独立的类中&#xff0c;使它…

UE5 C++ 创建可缩放的相机

一.要将相机设置在Pawn类里 1.在MyPawn头文件里&#xff0c;加上摇臂和相机组件 #include "GameFramework/SpringArmComponent.h" #include "Camera/CameraComponent.h" 2.在Pawm里声明SceneComponet&#xff0c;SpringArmComponent,CameraComponent组件…

新手入门C语言之其他操作符以及操作符属性

一.逗号操作符 1.形式&#xff1a; exp1, exp2, exp3 ......逗号表达式&#xff1a;由逗号隔开的一串表达式 2.执行过程&#xff1a;从左到右依次执行每一个表达式&#xff0c;表达式的结果是最后一个表达式的结果 int main() {int a 1;int b 2;int c (a > b, a b…

给定长度为n(n<=20)的数组a,-20<=ai<=20, 每次操作选择i,j,使a[i] += a[j], 在31次操作内使a不递减,输出每次操作的i,j

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5, …

SpringBoot项目实现文件上传,MINIO+OSS阿里云

MINIO 安装以及部署 官网&#xff1a;MinIO | Code and downloads to create high performance object storage 下载后是一个minio.exe的文件&#xff0c;可以先创一个文件夹来存放数据以及文件 在文件的目录下cmd进入控制台 minio.exe server data 启动成功后控制台会打印账…

代码随想录算法训练营第59天 | 583.两个字符串的删除操作 + 72.编辑距离 + 编辑距离总结篇

今日任务 583. 两个字符串的删除操作 72. 编辑距离 编辑距离总结篇 583.两个字符串的删除操作 - Medium 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以…

IntelliJ IDEA 创建Spring Boot 项目整合jdbc详细步骤

IntelliJ IDEA 创建Spring Boot 项目&整合jdbc详细步骤 1、打开 IntelliJ IDEA 软件2、使用 "Spring Initializr" 作为项目类型&#xff0c;新建项目工程3、选择对应的SpringBoot版本和依赖4、Spring Boot 项目的结构5、创建一个TestController&#xff0c;并运行…

个性化纹身设计,Midjourney带你探索独一无二的艺术之美

hello,大家好&#xff0c;欢迎回来。 在当今社会&#xff0c;纹身已经变得非常常见。 在寻求与众不同的个性化纹身时&#xff0c;你是否曾经为了找不到独特的设计而苦恼&#xff1f; 现在&#xff0c;Midjourney将为你打开一扇全新的艺术之门&#xff0c;引领你探索纹身设计…

matplotlib绘图初步

文章目录 绘制曲线图完整流程图像属性 绘制曲线图 matplotlib是python中最常用的可视化库&#xff0c;提供了不同坐标系下的二十余种常用图像&#xff0c;并且提供了动态图像绘制的方法&#xff0c;可以满足科学计算中的绝大多数可视化需求。而在matplotlib中&#xff0c;绝大…

基于Redis限流(固定窗口、滑动窗口、漏桶、令牌桶)(肝货!!!)

近期redis复习的比较多&#xff0c;在限流这方面发现好像之前理解的限流算法有问题&#xff0c;索性花了一天“带薪摸鱼”时间肝了一天&#xff0c;有问题可以评论区探讨。 废话不多说&#xff0c;正片开始 目录 Maven固定窗口滑动窗口算法漏桶算法令牌桶算法 Maven 有些不用的…

[HTML]Web前端开发技术28(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;佬佬会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…