306. Java Stream API - 流特性
在 Java Stream API 中,每个流(Stream)都具有一些特性,这些特性定义了流如何处理数据,以及这些数据是否具有某些特定的属性。这些特性对编写高效的流管道非常重要,尤其是在使用并行流时。
✅ 什么是 Stream 的特性?
Stream API 使用一个特殊的对象——Spliterator,来描述流的特性。Spliterator 接口的名字来源于它在流处理中的作用,它的功能类似于迭代器(Iterator)在集合(Collection)中的作用。此外,Spliterator 还控制并行流如何将元素分配到不同的 CPU 上进行处理。
🧠 特性概述:
- ORDERED:流中的元素处理顺序是有意义的。
- DISTINCT:流中的元素没有重复项。
-
NONNULL:流中的元素没有
null值。 - SORTED:流中的元素是排序的。
- SIZED:流处理的元素数量是已知的。
- SUBSIZED:分割该流时,得到的子流也是 SIZED 的。
⚠️ 其他特性:
- IMMUTABLE 和 CONCURRENT 特性在此教程中未涉及。
流的特性由流的来源、所执行的操作以及该流是如何创建的来决定。理解这些特性有助于我们在开发中做出更加高效和有针对性的优化。
✅ 如何检查 Stream 的特性
如果你想检查一个流是否具备某种特性,可以通过 Spliterator 的 characteristics() 方法来实现。该方法返回一个包含多个标志位的整数,每个标志位代表流的不同特性。
例如,我们可以编写一个判断流是否具备 ORDERED 特性的谓词(Predicate):
Predicate<Stream<?>> isOrdered =
stream -> ((stream.spliterator().characteristics() & Spliterator.ORDERED) != 0);
Stream<Integer> stream = List.of(1, 2, 3).stream();
boolean ordered = isOrdered.test(stream);
System.out.println("ordered = " + ordered);
输出:
ordered = true
解释:
- 这段代码通过
spliterator()方法获取流的Spliterator,然后使用位运算来判断该流是否具有ORDERED特性。 - 如果流的来源是
List,那么它默认是有序的,因此ORDERED特性会被设置为true。 - 如果你将
List替换为Set,你会发现ORDERED特性不再存在,因为Set中的元素顺序是不可预测的。
✅ 详细介绍每个流特性
🎬 ORDERED(有序)
如果流是从有序的数据源创建的,那么它就是有序流。例如,List 接口的实例,以及 Files.lines(path) 和 Pattern.splitAsStream(string) 等方法都会产生有序流。
对于有序流,元素的顺序是非常重要的。然而,在并行流中保留元素顺序可能会带来性能开销。若你不关心顺序,可以通过 unordered() 中间操作移除流的 ORDERED 特性。
示例:判断流是否为有序流
Stream<String> orderedStream = List.of("apple", "banana", "cherry").stream();
boolean isOrdered = isOrdered.test(orderedStream);
System.out.println("Is ordered: " + isOrdered);
输出:
Is ordered: true
对于 List 流,默认是有序的,因此返回 true。
移除顺序特性:
Stream<String> unorderedStream = orderedStream.unordered();
boolean isOrderedAfterUnordered = isOrdered.test(unorderedStream);
System.out.println("Is ordered after unordered: " + isOrderedAfterUnordered);
输出:
Is ordered after unordered: false
通过 unordered() 操作,我们可以将有序流转换为无序流,进而移除 ORDERED 特性。
🎬 DISTINCT(去重)
流中的元素是去重的,意味着没有重复的元素。如果流的元素可以被去重,那么该流具有 DISTINCT 特性。例如,Stream.of(1, 2, 2, 3) 会返回一个不包含重复项的流。
示例:检查流的去重特性
Stream<Integer> distinctStream = Stream.of(1, 2, 2, 3).distinct();
boolean isDistinct = distinctStream.allMatch(new HashSet<>()::add);
System.out.println("Is distinct: " + isDistinct);
输出:
Is distinct: true
🎬 NONNULL(无 null 元素)
如果流中的元素不包含 null,那么该流具有 NONNULL 特性。Stream.of(1, 2, 3) 就是一个没有 null 元素的流。
示例:检查流是否包含 null 元素
Stream<String> nonNullStream = Stream.of("apple", "banana", "cherry");
boolean hasNull = nonNullStream.anyMatch(Objects::isNull);
System.out.println("Contains null: " + hasNull);
输出:
Contains null: false
🎬 SORTED(已排序)
如果流的元素是有序的,那么该流具有 SORTED 特性。这意味着流中的元素是按照某种规则排序的,例如按升序或降序排列。
示例:检查流是否已排序
Stream<Integer> sortedStream = Stream.of(3, 1, 2).sorted();
boolean isSorted = sortedStream.isOrdered();
System.out.println("Is sorted: " + isSorted);
输出:
Is sorted: true
🎬 SIZED(已知大小)
如果流的大小是已知的,即流处理的元素数量是确定的,那么该流具有 SIZED 特性。比如,List 和 Set 流都是有大小的。
✅ 总结
了解和利用流的特性,可以帮助我们在编写高效的流管道时做出更好的决策。例如,知道一个流是否是有序的,可以让我们在并行流处理中避免不必要的性能开销。如果你不关心元素的顺序,使用 unordered() 可以显著提高并行流的性能。
通过 Spliterator 获取流的特性,我们可以为流操作添加更细粒度的优化,尤其是在处理大量数据时,能让程序运行更加高效。