如何理解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. 并行流的适用性 :并行流适合处理大规模数据,但对于小规模数据或复杂操作,顺序流可能更高效。
相关推荐
uhakadotcom1 小时前
Python 量化计算入门:基础库和实用案例
后端·算法·面试
小萌新上大分1 小时前
SpringCloudGateWay
java·开发语言·后端·springcloud·springgateway·cloudalibaba·gateway网关
uhakadotcom2 小时前
使用Python获取Google Trends数据:2025年详细指南
后端·面试·github
uhakadotcom2 小时前
使用 Python 与 Google Cloud Bigtable 进行交互
后端·面试·github
直视太阳2 小时前
springboot+easyexcel实现下载excels模板下拉选择
java·spring boot·后端
追逐时光者2 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 33 期(2025年4.1-4.6)
后端·.net
灼华十一2 小时前
Golang系列 - 内存对齐
开发语言·后端·golang
兰亭序咖啡3 小时前
学透Spring Boot — 009. Spring Boot的四种 Http 客户端
java·spring boot·后端
Asthenia04123 小时前
深入解析Pandas索引机制:离散选择与聚合选择的差异及常见误区
后端