Java Stream API

2025年4月21日 | 阅读 16 分钟

Java 8 中引入了一个新的附加包 java.util.stream。该包包含类、接口和枚举,允许对元素进行函数式操作。您可以通过导入 java.util.stream 包来使用流。

Stream 提供以下特性

  • Stream 不存储元素。它只是将元素从数据结构、数组或 I/O 通道等源,通过一系列计算操作来传递。
  • Stream 本质上是函数式的。对流执行的操作不会修改其源。例如,过滤来自集合的 Stream 会生成一个不包含过滤元素的新 Stream,而不是从源集合中移除元素。
  • Stream 是惰性的,仅在需要时才评估代码。
  • Stream 的元素在 Stream 的生命周期中只访问一次。与 Iterator 类似,要重新访问源的相同元素,必须生成一个新的 Stream。

我们可以使用 Stream 来过滤、收集、打印以及将一种数据结构转换为另一种数据结构等。在下面的示例中,我们通过 Stream 应用了各种操作。

Stream 的各种核心操作

1. 中间操作

Java Stream 中的中间操作会返回另一个 Stream。它们通常用于转换或过滤原始 Stream 的元素。由于它们是惰性的,意味着在调用终端操作之前不执行任何处理,因此可以链接多个中间操作。

常用中间操作

  • map(Function<T, R>): 使用提供的函数将 Stream 中的每个元素转换为另一种形式。
  • filter(Predicate<T>): 根据指定条件从 Stream 中选择元素。
  • flatMap(Function<T, Stream<R>>): 通过应用返回每个元素的流的函数,将每个元素转换为零个或多个元素。
  • distinct(): 从 Stream 中删除重复的元素。
  • sorted(): 对 Stream 的元素进行排序。
  • limit(long n): 将 Stream 截断为不超过指定大小。
  • skip(long n): 跳过 Stream 的前 n 个元素。
  • peek(Consumer<T>): 在不消耗元素的情况下对 Stream 中的每个元素执行指定操作。

2. 终端操作

终端操作是消耗 Stream 并产生结果的操作,例如值、集合,甚至副作用。一旦调用了终端操作,Stream 就会被处理,并且无法重用。

常用终端操作

  • forEach(Consumer<T>): 对 Stream 中的每个元素执行操作。
  • collect(Collector<T, A, R>): 将 Stream 的元素减少为一个可变结果容器,例如列表或映射。
  • reduce(BinaryOperator<T>): 使用关联累积函数将 Stream 的元素减少为单个值。
  • count(): 返回 Stream 中的元素计数。
  • anyMatch(Predicate<T>): 如果 Stream 中的任何元素与给定谓词匹配,则返回 true。
  • allMatch(Predicate<T>): 如果 Stream 中的所有元素都与给定谓词匹配,则返回 true。
  • noneMatch(Predicate<T>): 如果 Stream 中的任何元素都不与给定谓词匹配,则返回 true。
  • findFirst(): 返回一个描述 Stream 中第一个元素的 Optional,如果 Stream 为空,则返回一个空的 Optional。
  • findAny(): 返回一个描述 Stream 中某个元素的 Optional,如果 Stream 为空,则返回一个空的 Optional。

3. 短路操作

短路操作是终端操作的一个子集,它们不需要处理整个 Stream 即可产生结果。它们可以从 Stream 处理管道中提前退出,从而节省计算时间。

常用短路操作

  • anyMatch(Predicate<T>): 如果任何元素与给定谓词匹配,则停止处理并返回 true。
  • allMatch(Predicate<T>): 如果任何元素不与给定谓词匹配,则停止处理并返回 false。
  • noneMatch(Predicate<T>): 如果没有元素与给定谓词匹配,则停止处理并返回 true。
  • findFirst(): 返回在 Stream 中遇到的第一个元素,然后停止处理。
  • findAny(): 返回在 Stream 中遇到的任何元素,然后停止处理。

Java Stream 接口方法

方法描述
boolean allMatch(Predicate<? super T> predicate)返回此 Stream 中所有与提供的谓词匹配的元素。如果 Stream 为空,则返回 true,并且不评估谓词。
boolean anyMatch(Predicate<? super T> predicate)返回此 Stream 中与提供的谓词匹配的任何元素。如果 Stream 为空,则返回 false,并且不评估谓词。
static <T> Stream.Builder<T> builder()返回一个 Stream 的生成器。
<R,A> R collect(Collector<? super T,A,R> collector)使用 Collector 对此 Stream 的元素执行可变归约操作。Collector 封装了用作 collect(Supplier, BiConsumer, BiConsumer) 参数的函数,允许重用收集策略和组合收集操作,例如多级分组或分区。
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)对此 Stream 的元素执行可变归约操作。可变归约是指归约值是可变结果容器(例如 ArrayList),并且通过更新结果的状态而不是替换结果来合并元素。
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)创建一个惰性连接的 Stream,其元素是第一个 Stream 的所有元素,后跟第二个 Stream 的所有元素。如果两个输入 Stream 都有序,则结果 Stream 也有序;如果任一输入 Stream 是并行的,则结果 Stream 是并行的。当关闭结果 Stream 时,将调用两个输入 Stream 的关闭处理程序。
long count()返回此 Stream 中的元素计数。这是归约的一种特殊情况。
Stream<T> distinct()返回此 Stream 中不重复的(根据 Object.equals(Object))元素组成的 Stream。
static <T> Stream<T> empty()返回一个空的顺序 Stream。
Stream<T> filter(Predicate<? super T> predicate)返回此 Stream 中与给定谓词匹配的元素组成的 Stream。
Optional<T> findAny()返回一个描述 Stream 中某个元素的 Optional,如果 Stream 为空,则返回一个空的 Optional。
Optional<T> findFirst()返回一个描述此 Stream 中第一个元素的 Optional,如果 Stream 为空,则返回一个空的 Optional。如果 Stream 没有出现顺序,则可能返回任何元素。
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)通过对每个元素应用提供的映射函数,将此 Stream 的每个元素替换为映射 Stream 的内容,从而得到一个 Stream。每个映射的 Stream 在其内容已放入此 Stream 后都会关闭。(如果映射的 Stream 为 null,则使用一个空 Stream。)
DoubleStream flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)通过对每个元素应用提供的映射函数,将此 Stream 的每个元素替换为映射 Stream 的内容,从而得到一个 DoubleStream。每个映射的 Stream 在其内容已放入此 Stream 后都会关闭。(如果映射的 Stream 为 null,则使用一个空 Stream。)
IntStream flatMapToInt(Function<? super T,? extends IntStream> mapper)通过对每个元素应用提供的映射函数,将此 Stream 的每个元素替换为映射 Stream 的内容,从而得到一个 IntStream。每个映射的 Stream 在其内容已放入此 Stream 后都会关闭。(如果映射的 Stream 为 null,则使用一个空 Stream。)
LongStream flatMapToLong(Function<? super T,? extends LongStream> mapper)通过对每个元素应用提供的映射函数,将此 Stream 的每个元素替换为映射 Stream 的内容,从而得到一个 LongStream。每个映射的 Stream 在其内容已放入此 Stream 后都会关闭。(如果映射的 Stream 为 null,则使用一个空 Stream。)
void forEach(Consumer<? super T> action)对此 Stream 的每个元素执行一个操作。
void forEachOrdered(Consumer<? super T> action)对此 Stream 的每个元素执行一个操作,如果 Stream 具有定义的出现顺序,则按该顺序执行。
static <T> Stream<T> generate(Supplier<T> s)返回一个无限顺序的无序 Stream,其中每个元素都由提供的 Supplier 生成。这适用于生成常量 Stream、随机元素 Stream 等。
static <T> Stream<T> iterate(T seed,UnaryOperator<T> f)返回一个无限顺序的有序 Stream,该 Stream 通过对初始元素 seed 的函数 f 进行迭代应用而生成,生成一个包含 seed, f(seed), f(f(seed)) 等的 Stream。
Stream<T> limit(long maxSize)返回此 Stream 的元素组成的 Stream,截断为长度不超过 maxSize。
<R> Stream<R> map(Function<? super T,? extends R> mapper)返回此 Stream 的元素应用给定函数的结果组成的 Stream。
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)返回此 Stream 的元素应用给定函数的结果组成的 DoubleStream。
IntStream mapToInt(ToIntFunction<? super T> mapper)返回此 Stream 的元素应用给定函数的结果组成的 IntStream。
LongStream mapToLong(ToLongFunction<? super T> mapper)返回此 Stream 的元素应用给定函数的结果组成的 LongStream。
Optional<T> max(Comparator<? super T> comparator)根据提供的 Comparator 返回此 Stream 的最大元素。这是归约的一种特殊情况。
Optional<T> min(Comparator<? super T> comparator)根据提供的 Comparator 返回此 Stream 的最小元素。这是归约的一种特殊情况。
boolean noneMatch(Predicate<? super T> predicate)返回此 Stream 中与提供的谓词匹配的元素。如果 Stream 为空,则返回 true,并且不评估谓词。
@SafeVarargs static <T> Stream<T> of(T... values)返回一个包含指定值的顺序 Stream。
static <T> Stream<T> of(T t)返回一个包含单个元素的顺序 Stream。
Stream<T> peek(Consumer<? super T> action)返回此 Stream 的元素组成的 Stream,并在从结果 Stream 中消耗元素时对每个元素执行提供的操作。
Optional<T> reduce(BinaryOperator<T> accumulator)对此 Stream 的元素执行归约,使用关联累积函数,并返回一个描述归约值的 Optional(如果存在)。
T reduce(T identity, BinaryOperator<T> accumulator)对此 Stream 的元素执行归约,使用提供的初始值和关联累积函数,并返回归约值。
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)对此 Stream 的元素执行归约,使用提供的初始值、累积和组合函数。
Stream<T> skip(long n)返回此 Stream 中在丢弃 Stream 的前 n 个元素后剩余的元素组成的 Stream。如果此 Stream 包含的元素少于 n 个,则将返回一个空 Stream。
Stream<T> sorted()返回此 Stream 的元素组成的 Stream,按自然顺序排序。如果此 Stream 的元素不可比较,则在执行终端操作时可能会抛出 java.lang.ClassCastException。
Stream<T> sorted(Comparator<? super T> comparator)返回此 Stream 的元素组成的 Stream,按提供的 Comparator 排序。
Object[] toArray()返回包含此 Stream 中元素的一个数组。
<A> A[] toArray(IntFunction<A[]> generator)返回包含此 Stream 中元素的一个数组,使用提供的生成器函数来分配返回的数组,以及可能用于分区执行或调整大小的任何其他数组。

Java 示例:不使用 Stream 过滤集合

在以下示例中,我们不使用 Stream 来过滤数据。这是在 Stream 包发布之前我们就使用的方法。

示例

编译并运行

输出

[25000.0, 28000.0, 28000.0]

Java Stream 示例:使用 Stream 过滤集合

在这里,我们使用 Stream 来过滤数据。我们可以看到代码得到了优化和维护。Stream 提供快速执行。

示例

编译并运行

输出

[90000.0]

Java Stream 迭代示例

我们可以使用 Stream 进行任意次数的迭代。Stream 提供了预定义的方法来处理我们实现的逻辑。在以下示例中,我们正在迭代、过滤并设置限制来固定迭代次数。

示例

编译并运行

输出

5
10
15
20
25

Java Stream 示例:过滤和迭代集合

在以下示例中,我们使用 filter() 方法。这里,我们可以看到代码得到了优化,并且非常简洁。

示例

编译并运行

输出

Dell Laptop

Java Stream 示例:集合中的 reduce() 方法

此方法接受一系列输入元素,并通过重复的操作将它们组合成一个单一的汇总结果。例如,计算数字的总和,或将元素累积到列表中。

在以下示例中,我们使用 reduce() 方法,该方法用于计算所有产品价格的总和。

示例

编译并运行

输出

201000.0
201000.0

Java Stream 示例:使用 Collectors 方法求和

我们还可以使用 Collectors 来计算数值的总和。在以下示例中,我们使用 Collectors 类及其指定的方法来计算所有产品价格的总和。

示例

编译并运行

输出

201000.0

Java Stream 示例:查找最大和最小产品价格

以下示例通过使用 Stream 来查找最小和最大产品价格。它提供了一种方便的方式来查找值,而无需使用命令式方法。

示例

编译并运行

输出

90000.0
25000.0

Java Stream 示例:集合中的 count() 方法

示例

编译并运行

输出

3

Stream 允许我们将结果收集成各种形式。我们可以将结果获取为 Set、List 或 Map,并对元素进行操作。

Java Stream 示例:将 List 转换为 Set

示例

编译并运行

输出

[25000.0, 28000.0]

Java Stream 示例:将 List 转换为 Map

示例

编译并运行

输出

{1=HP Laptop, 2=Dell Laptop, 3=Lenevo Laptop, 4=Sony Laptop, 5=Apple Laptop}

Stream 中的方法引用

示例

编译并运行

输出

[90000.0]

Java Stream 特化

Java Streams API 为高效处理原始数据类型提供了专门的 Stream 类型:IntStream 用于整数,LongStream 用于长整数,DoubleStream 用于双精度浮点数。这些特化提供了更高的性能,并配备了为数值计算量身定制的其他操作,这使得它们非常适合涉及原始数字的操作。

1. 特化操作

特化 Stream 带有在处理数值数据时特别有用的操作。这些操作超出了通用 Stream <T> 接口中可用的操作,提供了直接在原始 Stream 上进行算术计算的方法。

2. 归约操作

归约操作是一种终端操作,它将 Stream 的所有元素组合成一个单一的结果。这些操作使用二元运算符重复应用一个函数,将结果作为函数应用于下一个 Stream 元素的每次后续应用中的第一个参数。这种模式对于汇总或聚合数值数据特别强大。

Java Stream:文件操作

Java Streams API(在 Java 8 中引入)提供了一种非常高效且表达力强的方式来处理数据流,包括文件。在此上下文中,“流”是指支持顺序和并行聚合操作的元素序列,而不是与 I/O 流中的 InputStreamOutputStream 混淆。

使用 Streams API 操作文件数据涉及从文件读取,逐行或批量处理数据,并经常将结果写入另一个文件。这通常通过 java.nio.file 包中的 Files 类来实现,该类与 Streams API 无缝集成。

示例

输入

输出

Read line: hello
Read line: world
Read line: Yes
Read line: 12345
Read line: Spaces
Read line: Java!
Read line: 1234
Read line: ABCDE
Filtered strings with length 5 (converted to uppercase): [HELLO, WORLD, 12345, JAVA!, ABCDE]

示例

输出

Text has been successfully written to the file.

Java 8 Stream