如何理解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. 并行流的适用性 :并行流适合处理大规模数据,但对于小规模数据或复杂操作,顺序流可能更高效。
相关推荐
阿宙ppppp13 分钟前
基于yolov5+LPRNet+flask+vue的车牌识别(1)
后端·图像识别
Java水解1 小时前
Spring AI模块化RAG架构解析:三阶段设计与实现详解
后端·spring
蓝倾1 小时前
京东商品SKU数据采集方式及接口说明
前端·后端·api
SimonKing1 小时前
一文搞定:SpringBoot集成语音识别模型FunASR
java·人工智能·后端
wenb1n1 小时前
【Nginx】Nginx进阶指南:解锁代理与负载均衡的多样玩法
后端
Undoom1 小时前
基于 Claude Code 与 BrowserCat MCP 的浏览器自动化全链路构建实践
后端
MacroZheng2 小时前
换掉Navicat!一款集成AI功能的数据库管理工具,功能真心强大!
java·后端·mysql
不甘打工的程序猿2 小时前
nacos融合spring cloud学习【Spring-Cloud-Alibaba】
后端·架构
Oriel2 小时前
在 Strapi v4 中生成专业 API 文档
前端·后端
jack_yin2 小时前
github万星Loki 项目 OOM 问题的一次实战
后端