如何理解Java中的 stream ?

Java 8 引入了 Stream API ,这是 Java 集合框架的一个重要扩展。Stream 流提供了一种高效、函数式的方式来处理集合数据(如 ListSetMap 等),它允许开发者以声明式的方式对数据进行操作,而不是通过传统的迭代方式。

以下是对 Java 8 中 Stream 流的详细理解:


1. Stream 的核心概念

(1) 什么是 Stream?

  • Stream 是一个来自数据源(如集合、数组、文件等)的元素序列,支持顺序或并行的聚合操作。
  • 它不是一种数据结构,而是一种用于处理数据的操作管道。
  • Stream 不会存储数据,而是对数据进行操作,并返回结果。

(2) Stream 的特点

  • 惰性求值(Lazy Evaluation)

    • Stream 的操作分为 中间操作(Intermediate Operations)终端操作(Terminal Operations)
    • 中间操作是惰性的,只有在终端操作触发时才会执行。
  • 不可变性

    • Stream 的操作不会修改原始数据源,而是生成一个新的 Stream 或结果。
  • 一次使用

    • Stream 只能被消费一次,再次使用需要重新创建。
  • 支持并行处理

    • Stream 提供了并行流(parallelStream),可以利用多核 CPU 并行处理数据。

2. Stream 的操作流程

Stream 的操作通常分为以下几个步骤:

  1. 创建 Stream :从数据源(如集合、数组)中获取 Stream。
  2. 中间操作 :对 Stream 进行一系列转换操作(如过滤、映射、排序等)。
  3. 终端操作 :触发 Stream 的执行,并生成结果(如收集到集合、计算总和等)。

3. Stream 的创建

(1) 从集合创建 Stream

List 复制代码
>Stream<String> stream = list.stream(); // 创建顺序流

Stream<String> parallelStream = list.parallelStream(); // 创建并行流

(2) 从数组创建 Stream

ini 复制代码
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);

(3) 从值创建 Stream

java

arduino 复制代码
Stream<String> stream = Stream.of("a", "b", "c");

(4) 从文件创建 Stream

java

js 复制代码
Stream<String> lines = Files.lines(Paths.get("file.txt"));

(5) 从生成器创建 Stream

java

arduino 复制代码
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5); // 生成 0, 2, 4, 6, 8
Stream<Double> randomStream = Stream.generate(Math::random).limit(5); // 生成 5 个随机数

4. 中间操作(Intermediate Operations)

中间操作返回一个新的 Stream,它们是惰性的,只有在终端操作触发时才会执行。

(1) filter

过滤符合条件的元素。

ini 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

numbers.stream()

.filter(n -> n % 2 == 0)

.forEach(System.out::println); // 输出 2, 4

(2) map

将每个元素映射为另一种形式。

java

rust 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

names.stream()

.map(String::toUpperCase)

.forEach(System.out::println); // 输出 ALICE, BOB, CHARLIE

(3) flatMap

将每个元素映射为多个元素,并将结果扁平化为单一流。

java

scss 复制代码
List<List<Integer>> nestedList = Arrays.asList(

Arrays.asList(1, 2),

Arrays.asList(3, 4)

);

nestedList.stream()

.flatMap(List::stream)

.forEach(System.out::println); // 输出 1, 2, 3, 4

(4) distinct

去除重复元素。

java

scss 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4);

numbers.stream()

.distinct()

.forEach(System.out::println); // 输出 1, 2, 3, 4

(5) sorted

对元素进行排序。

java

scss 复制代码
List<Integer> numbers = Arrays.asList(4, 2, 5, 1, 3);

numbers.stream()

.sorted()

.forEach(System.out::println); // 输出 1, 2, 3, 4, 5

(6) peek

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

java

scss 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3);

numbers.stream()

.peek(n -> System.out.println("Before: " + n))

.map(n -> n * 2)

.peek(n -> System.out.println("After: " + n))

.collect(Collectors.toList());

5. 终端操作(Terminal Operations)

终端操作触发 Stream 的执行,并生成结果。

(1) forEach

遍历流中的每个元素。

java

ini 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

names.stream().forEach(System.out::println);

(2) collect

将流的结果收集到集合或其他数据结构中。

java

ini 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

List<String> upperCaseNames = names.stream()

.map(String::toUpperCase)

.collect(Collectors.toList());

(3) reduce

对流中的元素进行归约操作。

java

ini 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

int sum = numbers.stream()

.reduce(0, Integer::sum); // 结果为 15

(4) count

统计流中元素的数量。

java

long count = numbers.stream().count();

(5) anyMatch / allMatch / noneMatch

检查流中是否满足某种条件。

java

ini 复制代码
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0); // 是否存在偶数
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0); // 是否全部为偶数

boolean noneEven = numbers.stream().noneMatch(n -> n % 2 == 0); // 是否不存在偶数

(6) findFirst / findAny

查找第一个或任意一个元素。

java

ini 复制代码
Optional<Integer> first = numbers.stream().findFirst();
Optional<Integer> any = numbers.stream().findAny();

6. 并行流(Parallel Streams)

并行流利用多核 CPU 并行处理数据,适合处理大量数据。

java

ini 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();

需要注意的是,并行流并不总是更快,因为它引入了线程管理的开销。对于小规模数据或复杂操作,顺序流可能更高效。


7. Stream 的优点

  1. 简洁性 :Stream 提供了声明式的操作方式,代码更加简洁易读。
  2. 可组合性 :中间操作可以链式调用,形成复杂的操作管道。
  3. 并行支持 :轻松实现并行处理,提升性能。
  4. 延迟执行 :中间操作只在终端操作触发时执行,避免不必要的计算。

8. Stream 的注意事项

  1. 一次性使用 :Stream 只能被消费一次,再次使用需要重新创建。
  2. 无状态操作优先 :尽量避免有状态操作(如 sorted),因为它们可能会影响性能。
  3. 避免副作用 :Stream 操作应该是纯函数式的,避免修改外部变量。
  4. 并行流的适用性 :并行流适合处理大规模数据,但对于小规模数据或复杂操作,顺序流可能更高效。
相关推荐
小鸡脚来咯32 分钟前
SpringBoot 常用注解通俗解释
java·spring boot·后端
豌豆花下猫40 分钟前
Python 3.14 t-string 要来了,它与 f-string 有何不同?
后端·python·ai
小奏技术1 小时前
Spring7将正式弃用Junit 4,我们也是时候迁移到Junit5了
后端
吴佳浩1 小时前
Python入门指南(四)-项目初始化
人工智能·后端·python
一颗知足的心1 小时前
Go语言之路————指针、结构体、方法
开发语言·后端·golang
Rabbb3 小时前
C# JSON属性排序、比较 Newtonsoft.Json
后端
蓝易云3 小时前
在Linux、CentOS7中设置shell脚本开机自启动服务
前端·后端·centos
一千柯橘3 小时前
Nestjs 解决 request entity too large
javascript·后端
userkang3 小时前
消失的前后端,崛起的智能体
前端·人工智能·后端·ai·硬件工程