Java 8 中 Stream 流的基础概念及特性介绍

embedded/2024/9/23 14:27:49/

1. 【Java 8 中 Stream 流的基础概念及特性介绍】

第一章:什么是Stream流

1.1 为什么引入Stream流

  • 传统的集合操作需要通过循环或迭代器进行,代码冗长且不易维护
  • Stream流提供了一种更简洁、更易读的方式处理集合数据
  • 引入Stream流可以实现更多的函数式编程范式,提高代码的可读性和可维护性

1.2 Stream流的定义和概念

概念说明
流的生成与操作Stream流是提供了一组元素的序列,在Java中由java.util.Stream接口表示
流的延迟求值特性Stream流的操作一般分为中间操作和终端操作,中间操作会返回一个新的Stream流,直到执行终端操作时才会触发实际的计算
1.2.1 流的生成与操作
  • Stream流可以通过集合、数组、静态工厂方法等方式进行生成
  • 操作包括筛选、映射、筛选去重、截断、排序等中间操作
1.2.2 流的延迟求值特性
  • Stream流的中间操作不会立即执行,只有在终端操作调用时才会触发计算
  • 延迟求值特性可以优化性能,避免不必要的计算

通过以上介绍,可以初步了解Java 8中Stream流的基本概念和原理。接下来将深入学习Stream流的基本操作和特性,进一步探索其在实际应用中的优势和灵活性。

2. Stream流的基本操作

在Java 8中,Stream流提供了强大的功能来对集合进行操作,可以非常方便地进行过滤、映射、排序等操作。在这一章节中,我们将深入介绍Stream流的基本操作,包括中间操作和终端操作的区别,以及不同类型的操作示例。

2.1 中间操作与终端操作的区别

Stream流的操作可以分为中间操作和终端操作两种。中间操作是流的数据处理阶段,不会立即执行,而是返回另一个流;而终端操作会触发流的处理并生成最终结果。下面我们将详细介绍中间操作和终端操作的不同类型。

2.1.1 过滤与映射操作

过滤操作可以根据指定的条件过滤出符合条件的元素,映射操作则可以将流中的元素映射为另一种形式。下面是一个示例代码,演示如何使用过滤和映射操作:

java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = numbers.stream().filter(num -> num % 2 == 0) // 过滤出偶数.map(num -> num * num) // 将偶数平方.collect(Collectors.toList());System.out.println(result); // 输出:[4, 16]

通过上面的代码,我们可以看到,先使用filter方法对偶数进行过滤,然后使用map方法将偶数平方,最后通过collect方法将结果收集到列表中。

2.1.2 排序与限制操作

排序操作可以对流中的元素进行排序,限制操作可以限制流的元素个数。下面是一个示例,展示如何对流进行排序和限制:

java">List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> result = names.stream().sorted() // 对姓名排序.limit(2) // 限制输出前两个.collect(Collectors.toList());System.out.println(result); // 输出:[Alice, Bob]

通过以上代码,我们可以看到,对姓名进行排序后,限制输出前两个名字,最终结果为[Alice, Bob]。

2.2 终端操作的几种类型

终端操作是流的最后一个阶段,触发流的处理并生成最终结果。在Java 8中,常见的终端操作包括forEachcollectreduce等。

2.2.1 forEach 和 forEachOrdered

forEach方法可以对流中的每个元素执行指定操作,forEachOrdered则保证元素的顺序。下面是一个简单示例:

java">List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry");
fruits.stream().forEach(fruit -> System.out.println("I like " + fruit));

通过上面的代码,我们可以对每个水果执行打印操作,输出每个水果的喜欢程度。

2.2.2 collect 和 reduce

collect方法将流中的元素收集到集合中,reduce方法可以将流中的元素进行计算。下面是一个展示计算流中元素和的示例:

java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);System.out.println("Sum of numbers: " + sum); // 输出:Sum of numbers: 15

在上面的代码中,我们使用reduce方法计算流中所有元素的和,结果为15。

通过本章节的介绍,我们了解了Stream流的基本操作,包括中间操作和终端操作的区别,以及不同类型的操作示例,希望可以帮助大家更好地使用Stream流来处理集合数据。

3. Stream流的特性探索

在Java 8中,Stream流不仅提供了丰富的基本操作方法,还具备了一些高级特性,本章将深入探索Stream流的特性,包括并行流与串行流的区别、流的自定义操作等。

3.1 并行流与串行流的区别

在Stream流的操作过程中,可以选择使用串行流(Sequential Stream)或并行流(Parallel Stream)。串行流意味着在单线程中依次执行流的各个阶段,而并行流则会在多个线程中同时执行流的不同阶段,从而提高处理数据的效率。下面我们来看一下两者的区别:

串行流的特点:

  • 依次执行流的每个操作,数据在流中单线程传递。
  • 编写简单,适用于处理较小数据量或操作不太复杂的情况。
  • 可以通过 .sequential() 方法将并行流转换为串行流。

并行流的特点:

  • 使用多线程同时处理流中的数据,加快数据处理速度。
  • 适用于处理大数据量或复杂操作场景。
  • 可以通过 .parallel() 方法将串行流转换为并行流。
3.1.1 使用并行流注意事项

在使用并行流时,需要注意以下几点来保证程序的正确性和性能:

  • 线程安全性: 确保操作流的数据结构是线程安全的,避免产生数据竞争。
  • 共享变量: 避免在并行流中使用共享变量,可以使用线程安全的数据结构或使用 reduce 操作来避免并发问题。
  • 任务分配: 流的并行化需要将任务分解为多个子任务,合适的任务划分可以最大程度地提高并行流的效率。

3.2 流的自定义操作

除了Java 8中提供的基本操作方法外,我们还可以自定义流的操作来满足特定需求。通过自定义操作,可以更灵活地处理数据流,在某些情况下,也能提高代码的可读性和复用性。

3.2.1 自定义收集器Collector接口

收集器(Collector)是用于在流的元素上执行汇总操作的接口,Java 8中提供了丰富的内置收集器,如toListtoSet等。除了内置收集器外,我们还可以通过实现Collector接口来自定义收集器,实现特定的汇总操作。

java">import java.util.stream.Collector;
import java.util.stream.Collectors;// 自定义收集器,将流中的元素拼接为字符串
public class StringCollector implements Collector<String, StringBuilder, String> {@Overridepublic Supplier<StringBuilder> supplier() {return StringBuilder::new;}@Overridepublic BiConsumer<StringBuilder, String> accumulator() {return StringBuilder::append;}@Overridepublic BinaryOperator<StringBuilder> combiner() {return StringBuilder::append;}@Overridepublic Function<StringBuilder, String> finisher() {return StringBuilder::toString;}@Overridepublic Set<Characteristics> characteristics() {return Collections.emptySet();}
}// 使用自定义收集器
List<String> words = Arrays.asList("Custom", "Collector", "Example");
String result = words.stream().collect(new StringCollector());
System.out.println(result); // Output: CustomCollectorExample

在上面的代码中,我们实现了一个自定义收集器StringCollector,用于将流中的字符串元素拼接为一个字符串。

3.2.2 自定义中间操作

除了自定义收集器外,我们还可以自定义中间操作(Intermediate Operation)来扩展Stream流的功能。通过自定义中间操作,可以在流中实现特定的数据处理逻辑,例如过滤、转换等。

java">import java.util.function.Function;
import java.util.stream.Stream;// 自定义中间操作,将字符串转换为大写并拼接
public class CustomOperation {public static void main(String[] args) {Stream<String> words = Stream.of("custom", "operation", "example");words = words.map(s -> s.toUpperCase()); // 转换为大写words = words.map(s -> s.concat("!")); // 拼接感叹号words.forEach(System.out::println); // 输出转换后的字符串}
}// 运行结果:
// CUSTOM!
// OPERATION!
// EXAMPLE!

上述代码中,我们自定义了一个中间操作map,实现将字符串转换为大写并在结尾追加感叹号的功能,以此来丰富流的处理方式。这种自定义中间操作可以让我们灵活处理流中的数据,实现更多定制化的操作。

通过自定义收集器和中间操作,我们可以更好地发挥Stream流的编程优势,实现更灵活、高效的数据处理方式。在实际项目中,合理应用自定义操作能够让代码更具可读性和可维护性,提高开发效率。

4. Stream流的实际应用

在本章中,我们将探讨如何在实际应用中利用Stream流简化集合操作和处理IO操作。通过Stream流,我们可以实现更加优雅和高效的数据处理方式,让代码更具可读性和维护性。

4.1 使用Stream流简化集合操作

在Java 8中,Stream流提供了丰富的API,可以帮助我们简化集合的操作,包括转换、过滤、映射等。下面我们来看一些使用Stream流的例子:

4.1.1 集合转换和过滤
java">List<String> names = Arrays.asList("Tom", "Jerry", "Alice", "Bob");// 使用Stream将名字转换为大写形式
List<String> upperCaseNames = names.stream().map(String::toUpperCase).collect(Collectors.toList());// 使用Stream过滤出长度大于3的名字
List<String> filteredNames = names.stream().filter(name -> name.length() > 3).collect(Collectors.toList());

通过以上代码,我们可以看到,使用Stream操作可以轻松地实现集合的转换和过滤,避免了传统的循环方式,让代码更具有函数式编程的特点。

4.1.2 面向对象和函数式编程对比

传统方式需要使用循环迭代集合,并在循环体中执行针对每个元素的操作,而函数式编程方式则是通过流的操作,将操作应用到整个集合上,实现了对集合的批量处理,让代码更为简洁和易读。

4.2 Stream流与IO操作

除了简化集合操作外,Stream流还可以用于处理IO操作,例如读写文件和处理网络流数据。下面我们来看一些使用Stream处理IO的例子:

4.2.1 读写文件使用Stream
java">try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {// 逐行读取文件内容并打印lines.forEach(System.out::println);
} catch (IOException e) {e.printStackTrace();
}try {// 写文件操作Files.write(Paths.get("output.txt"), "Hello, Stream!".getBytes());
} catch (IOException e) {e.printStackTrace();
}

通过以上代码,我们可以看到,使用Stream处理文件IO操作非常简洁和高效,通过流式操作读取和写入文件内容,让IO操作代码更为优雅。

4.2.2 处理网络流数据
java">try (Socket socket = new Socket("127.0.0.1", 8080);InputStream inputStream = socket.getInputStream();InputStreamReader reader = new InputStreamReader(inputStream);BufferedReader bufferedReader = new BufferedReader(reader)) {// 读取服务器返回的数据bufferedReader.lines().forEach(System.out::println);
} catch (IOException e) {e.printStackTrace();
}

在处理网络流数据时,Stream流同样可以发挥作用,通过流式操作从网络流中读取数据,并对数据进行处理,让网络编程更为简便和灵活。

通过以上示例,我们可以看到Stream流在实际应用中的广泛应用,无论是简化集合操作还是处理IO操作,都能为我们带来更好的编程体验和代码质量。流式处理的思想使得代码更为清晰和易于维护,值得开发者深入学习和应用。

总结

通过本章的学习,我们了解了如何使用Stream流简化集合操作和处理IO操作的方法,掌握了Stream流在实际应用中的优势和灵活性。在未来的开发中,我们可以更多地运用Stream流,提高代码的可读性和效率,实现更加优秀的程序设计。

5. Stream流的性能分析与优化

在本章中,我们将深入研究Java 8中Stream流的性能问题,并探讨如何通过优化来提高流操作的效率。

5.1 使用Stream流注意性能问题

在编写Stream流代码时,我们需要留意一些潜在的性能问题,避免因为不当的操作而导致性能下降。

  • 流的复用性问题

    • 由于流只能被消费一次,如果需要多次对同一数据集进行操作,应考虑将流内容保存在集合中,而不是反复创建流。
    • 示例代码:
      java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
      // 错误示例:多次使用同一流
      long count1 = numbers.stream().filter(n -> n > 2).count();
      long count2 = numbers.stream().filter(n -> n < 5).count();
      // 正确示例:将流保存在集合中重复使用
      Stream<Integer> numberStream = numbers.stream();
      long count1 = numberStream.filter(n -> n > 2).count();
      long count2 = numberStream.filter(n -> n < 5).count();
      
  • 避免多次遍历流

    • 在对流进行操作时,尽量避免多次遍历同一流,可以考虑使用集合或其他方式保存中间结果,以免重复执行流操作。
    • 示例代码:
      java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
      // 错误示例:多次遍历同一流
      long count1 = numbers.stream().filter(n -> n > 2).count();
      long count2 = numbers.stream().filter(n -> n < 5).count();
      // 正确示例:使用集合保存中间结果
      List<Integer> filteredNumbers = numbers.stream().filter(n -> n > 2).collect(Collectors.toList());
      long count1 = filteredNumbers.size();
      List<Integer> otherFilteredNumbers = numbers.stream().filter(n -> n < 5).collect(Collectors.toList());
      long count2 = otherFilteredNumbers.size();
      

5.2 如何优化Stream流操作

为了提高Stream流的性能,我们可以采取一些优化措施,以确保流操作的高效执行。

  • 流的短路优化

    • Stream流内部很多操作(如findFirst、allMatch等)都可以利用短路机制来提前结束计算,节省不必要的计算开销。
    • 示例代码:
      java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
      // 使用流的短路优化,找到第一个大于3的元素
      Optional<Integer> result = numbers.stream().filter(n -> n > 3).findFirst();
      if (result.isPresent()) {System.out.println("找到第一个大于3的元素:" + result.get());
      }
      
  • 使用并行流提高性能

    • 在对大数据集进行操作时,可以考虑使用并行流来利用多核处理器的优势,加快流操作的处理速度。
    • 示例代码:
      java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
      // 顺序流操作
      long sequentialTime = System.currentTimeMillis();
      long count = numbers.stream().filter(n -> n % 2 == 0).count();
      sequentialTime = System.currentTimeMillis() - sequentialTime;
      // 并行流操作
      long parallelTime = System.currentTimeMillis();
      long parallelCount = numbers.parallelStream().filter(n -> n % 2 == 0).count();
      parallelTime = System.currentTimeMillis() - parallelTime;
      System.out.println("顺序流操作耗时:" + sequentialTime + "ms,结果为:" + count);
      System.out.println("并行流操作耗时:" + parallelTime + "ms,结果为:" + parallelCount);
      

通过以上优化方法,可以有效提升Java 8中Stream流的性能,使流操作更加高效、快速。

总结

在对Java 8中Stream流进行性能分析和优化时,我们应该重点关注流的复用性和遍历次数,利用短路优化和并行流提高操作效率。通过合理的优化手段,可以提升Stream流操作的执行速度,使代码更具效率和可维护性。


http://www.ppmy.cn/embedded/9067.html

相关文章

Vue 组件通信的几种方式

vue通信方式简介 在Vue.js中&#xff0c;组件间通信可以通过props、$emit、事件总线、Vuex以及Provide/Inject等方式来实现&#xff0c;总的来说&#xff0c;组件通信是现代前端开发中不可或缺的一部分&#xff0c;它可以帮助开发者构建更加模块化、可维护和可扩展的应用。 Pr…

java的单元测试和反射

单元测试 就是针对最小的功能单元&#xff0c;编写测试代码对其进行正确性测试 Junit单元测试框架&#xff1a; 可以用来对方法进行测试 有点&#xff1a; 可以灵活的编写测试代码&#xff0c;可以针对某个方法进行测试&#xff0c;也支持一键完成对全部方法的自动发测试&a…

运气变好的几个小妙招

首先不要总是说一些丧气话&#xff0c;因为老天爷是听不到主语的&#xff0c;即使你当时说的是”谁谁谁好烦人啊“类似的句式&#xff0c;但传到上帝耳朵的时候会失去主语&#xff0c;也就会潜移默化的带到自己身上。 那我们应该怎么做呐&#xff1f; 1. 避免消极语言 首先&…

Linux中进程和计划任务管理(2)

一.进程命令 1.lsof lsof 命令&#xff0c;“list opened files”的缩写&#xff0c;直译过来&#xff0c;就是列举系统中已经被打开的文件。通过 lsof 命令&#xff0c;我们就可以根据文件找到对应的进程信息&#xff0c;也可以根据进程信息找到进程打开的文件。 格式&…

Uipath用计划任务启动 bat脚本语句

方法一 cd C:\Program Files\UiPath\Studio UiRobot execute --file "Uipath路径\project.json" 这种是用UiRobot.exe来启动任务的 方法二 "C:\Progra~1\UiPath\Studio\UiRobot.exe" -file "C:\Users\Documents\UiPath\se_Windows.1.0.3.nupkg&q…

MAC上如何将某个目录制作成iso格式磁盘文件,iso文件本质是什么?以及挂载到ParallelDesktop中?(hdiutil makehybrid )

背景 ParallelsDesktop没有安装ParallelsTools的无法共享目录&#xff0c;可以通过ParallelsDesktop提供CD磁盘的方式共享进去 命令 # 准备文档 mkdir mytestdir cp xxx mytestdir# 生成iso hdiutil makehybrid -o output.iso mytestdir -iso -joliethdiutil是MAC提供的磁盘…

【C++】explicit关键字详解(explicit关键字是什么? 为什么需要explicit关键字? 如何使用explicit 关键字)

目录 一、前言 二、explicit关键字是什么&#xff1f; 三、构造函数还具有类型转换的作用 &#x1f34e;单参构造函数 ✨引出 explicit 关键字 &#x1f34d;多参构造函数 ✨为什么需要explicit关键字&#xff1f; ✨怎么使用explicit关键字&#xff1f; 四、总结 五…

​LeetCode解法汇总377. 组合总和 Ⅳ

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 动态规划。dp[i]代表target为i时的类型数…