【Java 基础篇】Java Stream 流详解

news2025/1/12 3:46:51

在这里插入图片描述

Java Stream(流)是Java 8引入的一个强大的新特性,用于处理集合数据。它提供了一种更简洁、更灵活的方式来操作数据,可以大大提高代码的可读性和可维护性。本文将详细介绍Java Stream流的概念、用法和一些常见操作。

什么是Stream流?

在开始介绍Java Stream流之前,让我们先了解一下什么是流。流是一系列元素的序列,它可以在一次遍历的过程中逐个处理这些元素。在Java中,流是对数据的抽象,可以操作各种不同类型的数据源,如集合、数组、文件等。

Stream流的主要特点包括:

  • 链式调用:可以通过一系列的方法调用来定义对流的操作,使代码更具可读性。
  • 惰性求值:流上的操作不会立即执行,只有在遇到终端操作时才会触发计算。
  • 函数式编程:流操作使用了函数式编程的思想,可以通过Lambda表达式来定义操作。
  • 并行处理:可以轻松地将流操作并行化,充分利用多核处理器的性能。

创建Stream流

在使用Java Stream流之前,首先需要创建一个流。流可以从各种数据源中创建,包括集合、数组、文件等。

从集合创建流

可以使用集合的stream()方法来创建一个流。例如:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
Stream<String> stream = names.stream();

从数组创建流

可以使用Arrays.stream()方法来从数组中创建一个流。例如:

int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);

从文件创建流

可以使用Files.lines()方法来从文件中创建一个流。例如:

try (Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
    // 处理文件中的每一行数据
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}

流的操作

一旦创建了流,就可以对其进行各种操作。流的操作可以分为两类:中间操作和终端操作。

中间操作

中间操作是对流的一系列处理步骤,这些步骤会返回一个新的流,允许链式调用。中间操作通常用于对数据进行过滤、映射、排序等操作。一些常见的中间操作包括:

  • filter(Predicate<T> predicate):根据条件过滤元素。
  • map(Function<T, R> mapper):将元素映射为新的值。
  • sorted():对元素进行排序。
  • distinct():去重,去除重复的元素。
  • limit(long maxSize):限制流中元素的数量。
  • skip(long n):跳过流中的前n个元素。

例如,以下代码将对一个整数集合进行筛选、映射和排序操作:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> result = numbers.stream()
    .filter(n -> n % 2 == 0) // 过滤偶数
    .map(n -> n * 2)         // 映射为原来的2倍
    .sorted()                // 排序
    .collect(Collectors.toList()); // 收集结果

终端操作

终端操作是流的最后一步操作,它会触发对流的计算并产生一个最终的结果。终端操作通常包括:

  • forEach(Consumer<T> action):对流中的每个元素执行操作。
  • collect(Collector<T, A, R> collector):将流中的元素收集到一个容器中。
  • toArray():将流中的元素收集到数组中。
  • reduce(identity, accumulator):对流中的元素进行归约操作,返回一个值。
  • count():返回流中元素的数量。
  • min(comparator):返回流中的最小元素。
  • max(comparator):返回流中的最大元素。
  • allMatch(predicate):检查流中的所有元素是否都满足条件。
  • anyMatch(predicate):检查流中是否存在满足条件的元素。
  • noneMatch(predicate):检查流中是否没有元素满足条件。
  • findFirst():返回流中的第一个元素。
  • findAny():返回流中的任意一个元素。

终端操作是流的最后一步,一旦调用终端操作,流将被消耗,不能再被复用。

示例:从集合中筛选特定条件的元素

让我们通过一个示例来演示Java Stream流的使用。假设我们有一个包含学生对象的集合,每个学生对象都有姓名、年龄和成绩属性。我们想从集合中筛选出年龄大于18岁且成绩优秀的学生。

class Student {
    private String name;
    private int age;
    private double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public double getScore() {
        return score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

public class Main {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", 20, 90.0),
            new Student("Bob", 22, 85.5),
            new Student("Charlie", 19, 88.5),
            new Student("David", 21, 92.0),
            new Student("Eva", 18, 94.5)
        );

        List<Student> result = students.stream()
            .filter(student -> student.getAge() > 18 && student.getScore() >= 90.0)
            .collect(Collectors.toList());

        result.forEach(System.out::println);
    }
}

运行以上代码,将输出符合条件的学生信息:

Student{name='Alice', age=20, score=90.0}
Student{name='David', age=21, score=92.0}

并行流

Java Stream还提供了并行流的支持,可以充分利用多核处理器的性能。只需将普通流转换为并行流,即可实现并行化处理。

List<Student> result = students.parallelStream()
    .filter(student -> student.getAge() > 18 && student.getScore() >= 90.0)
    .collect(Collectors.toList());

需要注意的是,并行流在某些情况下可能会引发线程安全问题,因此在处理共享状态时要格外小心。

更多操作

当使用Java Stream流进行数据处理时,除了基本的过滤、映射、排序和归约等操作外,还有许多其他有用的中间操作和终端操作。在本节中,我将介绍一些常见的Stream流操作,帮助你更好地理解如何使用它们。

中间操作

1. distinct()

distinct()方法用于去除流中的重复元素,返回一个去重后的新流。

示例:

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

System.out.println(distinctNumbers); // 输出: [1, 2, 3, 4, 5]
2. limit(n)

limit(n)方法用于截取流中的前n个元素,返回一个包含前n个元素的新流。

示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> limitedNumbers = numbers.stream()
    .limit(5)
    .collect(Collectors.toList());

System.out.println(limitedNumbers); // 输出: [1, 2, 3, 4, 5]
3. skip(n)

skip(n)方法用于跳过流中的前n个元素,返回一个跳过前n个元素后的新流。

示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> skippedNumbers = numbers.stream()
    .skip(5)
    .collect(Collectors.toList());

System.out.println(skippedNumbers); // 输出: [6, 7, 8, 9, 10]
4. flatMap()

flatMap()方法用于将流中的每个元素映射成一个新的流,然后将这些新流合并成一个流。通常用于将嵌套的集合扁平化。

示例:

List<List<Integer>> nestedLists = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);

List<Integer> flattenedList = nestedLists.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList());

System.out.println(flattenedList); // 输出: [1, 2, 3, 4, 5, 6]

终端操作

1. forEach()

forEach()方法用于对流中的每个元素执行指定的操作。

示例:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
    .forEach(name -> System.out.println("Hello, " + name));
2. toArray()

toArray()方法用于将流中的元素收集到数组中。

示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer[] numberArray = numbers.stream()
    .toArray(Integer[]::new);
3. reduce(identity, accumulator)

reduce()方法用于对流中的元素进行归约操作,返回一个值。identity是初始值,accumulator是归约函数。

示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);

System.out.println(sum); // 输出: 15
4. collect()

collect()方法用于将流中的元素收集到一个集合或其他数据结构中。可以使用Collectors类提供的各种工厂方法创建不同类型的集合。

示例:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> collectedNames = names.stream()
    .collect(Collectors.toList());

Set<String> collectedSet = names.stream()
    .collect(Collectors.toSet());

Map<String, Integer> collectedMap = names.stream()
    .collect(Collectors.toMap(name -> name, String::length));
5. min(comparator)max(comparator)

min(comparator)max(comparator)方法用于查找流中的最小和最大元素,需要传入一个比较器(Comparator)来定义比较规则。

示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> minNumber = numbers.stream()
    .min(Integer::compareTo);

Optional<Integer> maxNumber = numbers.stream()
    .max(Integer::compareTo);

System.out.println(minNumber.orElse(0)); // 输出: 1
System.out.println(maxNumber.orElse(0)); // 输出: 5
6. anyMatch(predicate)allMatch(predicate)noneMatch(predicate)

这些方法用于检查流中的元素是否满足给定的条件。

  • anyMatch(predicate):检查流中是否有任意一个元素满足条件。
  • allMatch(predicate):检查流中的所有元素是否都满足条件。
  • noneMatch(predicate):检查流中是否没有元素满足条件。

示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyGreaterThanThree = numbers.stream()
    .anyMatch(n -> n > 3);

boolean allGreaterThanThree = numbers.stream()
    .allMatch(n -> n > 3);

boolean noneGreaterThanTen = numbers.stream()
    .noneMatch(n -> n > 10);

System.out.println(anyGreaterThanThree); // 输出: true
System.out.println(allGreaterThanThree); // 输出: false
System.out.println(noneGreaterThanTen);  // 输出: true
7. findFirst()findAny()

findFirst()方法返回流中的第一个元素(在串行流中通常是第一个元素,但在并行流中不确定),findAny()方法返回流中的任意一个元素。

示例:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> first = names.stream()
    .findFirst();

Optional<String> any = names.parallelStream()
    .findAny();

这些只是Java Stream流的一些常见操作,Stream API提供了更多的方法来处理数据。根据具体的需求,你可以组合这些操作来构建复杂的数据处理流程。希望这些示例能帮助你更好地理解和使用Java Stream流。

注意事项

在使用Java Stream流时,有一些注意事项需要考虑,以确保代码的正确性和性能。以下是一些常见的注意事项:

  1. 不可重用性: 一旦创建了一个Stream对象并执行了终端操作,该Stream就不能再被重用。如果需要对同一数据集进行多次处理,应该每次都创建新的Stream对象。

  2. 惰性求值: Stream是惰性求值的,中间操作只会在终端操作触发后才会执行。这意味着中间操作不会立即产生结果,而是在需要结果时才进行计算。这可以帮助节省计算资源,但也需要谨慎处理,以免产生意外的行为。

  3. 并行流的线程安全性: 如果使用并行流(parallelStream()),要确保Stream操作是线程安全的。一些操作可能会引发并发问题,需要适当的同步或避免使用并行流。

  4. 流的关闭: 如果你使用的是基于IO的流(如Files.lines()),需要确保在使用完后关闭流,以释放资源。

  5. 性能注意事项: Stream操作的性能可能会受到数据量的影响。在大数据集上使用Stream时,要注意性能问题,可以考虑使用并行流或其他优化方法。

  6. 空值处理: 在使用Stream时,要注意空值(null)的处理,避免空指针异常。可以使用filtermap等操作来过滤或转换空值。

  7. 有状态操作: 一些Stream操作是有状态的,例如sorteddistinct,它们可能需要缓存所有元素,因此在处理大数据集时要谨慎使用,以免导致内存溢出。

  8. 自定义收集器: 如果需要自定义收集器(Collector),要确保它的线程安全性和正确性,以便在Stream中使用。

  9. 不可变性: 推荐使用不可变对象和不可变集合来处理Stream,以避免并发问题。

  10. 了解Stream操作的复杂度: 不同的Stream操作具有不同的时间复杂度。了解操作的复杂度有助于选择最适合的操作来满足性能需求。

总之,使用Java Stream流可以编写更简洁和可读性强的代码,但在使用过程中需要考虑到流的惰性求值、线程安全性、性能等方面的注意事项,以确保代码的正确性和性能。

总结

Java Stream流是一项强大的特性,可以极大地简化集合数据的处理。通过中间操作和终端操作的组合,我们可以轻松地实现各种复杂的数据处理任务。同时,流还提供了并行处理的支持,可以充分利用多核处理器的性能。

要注意的是,流是一次性的,一旦调用了终端操作,流将被消耗,不能再被复用。此外,在使用并行流时要注意线程安全的问题。

希望本文能帮助你更好地理解和使用Java Stream流,提高代码的可读性和可维护性。如果你对Java Stream流还有更多的疑问或想要深入了解,可以查阅官方文档或进一步学习相关的教程和示例。 Happy coding!

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

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

相关文章

bootstrapv4轮播图去除两侧阴影及线框的方法

文章目录 前言一、前提条件&#xff1a;二、bootstrap文档组件展示与实际应用1.官方文档展示如下&#xff1a;没有阴影2.实际应用情况如下&#xff1a; 三、解决方案 前言 这篇文章主要介绍了bootstrapv4轮播图去除两侧阴影及线框的方法,本文通过示例代码给大家介绍的非常详细…

基于微信小程序的项目申报管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言用户微信端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉…

【Vue】vue-cli一站式搭建SPA项目

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Vue快速入门》。&#x1f3af;&#x1f3af; &…

vtk- 数据类型(一) 三角链实例代码

三角链实例代码 #include <iostream> #include <string> #include <regex> #include "tuex.h" #include "vtkCylinderSource.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkRendere…

Java --- MySQL8数据库优化性能查看工具

目录 一、数据库服务器优化步骤 ​编辑二、查看系统性能的参数 三、统计SQL的查询成本&#xff1a;last_query_cost 四、定位执行慢的SQL&#xff1a;慢日志查询 4.1、开启并设置慢查询日志查询参数 4.2、查看慢查询数目 4.3、慢查询日志分析工具&#xff1a;mysqldumps…

MySQL单表查询和多表查询

一、单表查询 素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作等 CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10)…

帆软FineReport决策报表之页面布局

最近在用帆软决策报表绘制首页大屏&#xff0c;记录使用过程&#xff0c;方便查看。 版本&#xff1a;FineReport10.0 第一步、页面布局 页面布局其实就是组件的排列组合&#xff0c;决策报表主区域body有两种布局方式&#xff1a;自适应布局和绝对布局。 1&#xff09;自适应…

C#中实现单元测试的示例流程_MSTest测试项目

一、单元测试简介 1.1、单元测试简介 在《单元测试艺术》一书中对于单元测试的定义是&#xff1a;【一个单元测试是一段代码&#xff0c;这段代码调用一个工作单元&#xff08;指&#xff1a;调用软件中的一个方法&#xff0c;这个方法执行过程中所发生的所有行为以及最后产生…

你好,python!——python基础中的基础

〇、注释 0.1 单行注释 python中用 “# ”来给某一行注释&#xff0c; 他的快捷键是 CTRL / &#xff0c;当注释内容已被注释时&#xff0c;会解除注释 0.2 多行注释 python中用 """ 注释内容 """ 来对内容进行多行注释 ""&q…

DATE和LocalDateTime在Java中有什么区别

在Java中&#xff0c;Date和LocalDateTime是两个表示日期和时间的类&#xff0c;它们有以下区别&#xff1a; 类型&#xff1a;Date是Java旧版提供的日期和时间类&#xff0c;而LocalDateTime是Java 8引入的新日期和时间API中的类。 不可变性&#xff1a;Date是可变类&#x…

Questasim与Visualizer的livesim仿真

1.前言 Live-Simulation (live-sim)模式允许Visualizer调试环境与Questasim进行交互操作&#xff0c;此模式将Visualizer GUI与Questasim仿真相结合&#xff0c;因此可以在线调试当前仿真的结果和波形。在livesim模式中&#xff0c;可以在Visualizer GUI中控制仿真、设置断点(…

【Java 基础篇】Java 类加载器详解

在Java编程中&#xff0c;类加载器&#xff08;Class Loader&#xff09;是一个重要的概念&#xff0c;它负责将类加载到Java虚拟机中&#xff0c;使程序能够正常运行。本文将详细解释Java类加载器的工作原理、不同类型的类加载器以及如何自定义类加载器。 什么是类加载器&…

智慧公厕以数字技术为中心,融合数字管理技术与城市治理需求精准对接

现代城市的发展离不开科技的进步&#xff0c;智慧公厕作为城市治理的重要方面之一&#xff0c;正逐渐受到越来越多的重视。智慧公厕以数字技术为中心&#xff0c;融合数字管理技术与城市治理需求精准对接&#xff0c;为城市居民提供更加方便、舒适的公共卫生设施。本文将以智慧…

2023年7月嵌入式项目开发专题总汇

一、前言 当前文章总结了28个基于不同单片机&#xff08;51单片机和STM32&#xff09;设计的软件和硬件项目。这些项目涵盖了计算器、手机、酒精检测仪、环境温度与湿度检测设备、考勤系统、门禁照相机、人体健康检测仪、数码相册、太阳能热水器、数显热水器和智能空调等多个领…

高性能计算环境下的深度学习异构集群建设与优化实践

★深度学习&#xff1b;模式识别&#xff1b;图像处理&#xff1b;人工智能建模&#xff1b;人工智能&#xff1b;深度学习算法&#xff1b;强化学习&#xff1b;神经网络&#xff1b;卷积神经网络&#xff1b;人工神经网络&#xff1b;VIBE算法&#xff1b;控制系统仿真&#…

用Redis做数据排名

1.背景 用Redis做数据缓存用的比较多&#xff0c;大家都能熟练使用String和Hash结构去存储数据&#xff0c;今天讲下如何使用ZSet来做数据排名。 假设场景是需要按天存储全国城市的得分数据&#xff0c;可以查询前十名的城市排名。 这个case可以使用传统关系型数据库做…

5-2 Pytorch中的模型层layers

深度学习模型一般由各种模型层组合而成。 torch.nn中内置了非常丰富的各种模型层。它们都属于nn.Module的子类&#xff0c;具备参数管理功能。 例如&#xff1a; nn.Linear, nn.Flatten, nn.Dropout, nn.BatchNorm2d, nn.Embedding nn.Conv2d,nn.AvgPool2d,nn.Conv1d,nn.ConvTr…

2023华为杯数学建模D题第三问-碳排放路径优化(能源消费结构调整的多目标优化模型构建详细过程+模型假设(可复制))

1.碳排放约束下&#xff08;人为干预按时碳达峰与碳中和的基准情景&#xff09;能源消费结构多目标优化模型构建 1.1基本假设 本文的模型设计主要基于以下几个基本假设&#xff1a; &#xff08;1&#xff09;能源消费结构调整的根本驱动要素&#xff0c;是对投资耗费的最小化…

uni-app图片保存到手机

uni.saveImageToPhotosAlbum(OBJECT) 官网地址uni.chooseImage(OBJECT) | uni-app官网 注意filePath不能是网络图片路径&#xff0c;如果我们是网络图片路径需要先下载文件资源到本地&#xff0c;然后再保存到相册 // 保存图片到手机 const saveImg () > {//wx.downloadF…

《计算机视觉中的多视图几何》笔记(10)

10 3D Reconstruction of Cameras and Structure 本章主要描述了如何利用2张图片来恢复相机的参数以及物体在三维空间中的形状。 文章目录 10 3D Reconstruction of Cameras and Structure10.1 Outline of reconstruction method10.2 Reconstruction ambiguity10.3 The proje…