309. Java Stream API - 非空流(NONNULL)
在 Java 中,非空流(NONNULL)指的是流中不包含任何 null 元素。
这种流非常适合在数据处理中需要排除 null 值的场景。
许多集合类型和流创建方法会天然地生成非空流。
✅ 创建非空流的常见场景
某些数据结构或方法生成的流会自动带有 Spliterator.NONNULL 特性,这意味着其元素保证不为 null。常见示例如下:
-
集合类型:
ArrayDeque、ArrayBlockingQueue、ConcurrentSkipListSet、ConcurrentHashMap.newKeySet()等集合类不允许插入null元素,因此它们生成的流天然是非空流。 -
文件与正则流:
-
Files.lines(path):读取文件行时,Stream<String>中不会出现null。 -
Pattern.splitAsStream(line):根据正则拆分字符串时,也不会产生null元素。
-
-
注意
Map.values():
尽管Map的keySet()和entrySet()通常不包含null,但values()可能包含null值(取决于存入的映射)。
因此,由map.values().stream()创建的流不一定是非空流。
🧩 示例:检查流是否具有 NONNULL 特性
Predicate<Stream<?>> isNonNull =
stream -> (stream.spliterator().characteristics() & Spliterator.NONNULL) != 0;
Map<Integer, String> hashMap = new HashMap<>();
Collection<String> values = hashMap.values();
System.out.println("Values from hashMap is non-null? " + isNonNull.test(values.stream()));
Collection<String> queue = new ArrayDeque<>();
System.out.println("ArrayDeque is non-null? " + isNonNull.test(queue.stream()));
输出:
Values from hashMap is non-null? false
ArrayDeque is non-null? true
解释:
-
hashMap.values():Map.values()可能包含null元素,因此其流不具备NONNULL特性。 -
ArrayDeque:该集合禁止null元素,因此由它创建的流天然是非空流。
✅ 非空流的特性
🌟 特点
- 流中的每个元素都保证非空(不为
null)。 - 通常来自于源头保证不含
null的集合或数据源。 - 可通过
Spliterator.NONNULL标识判断。
🔧 操作特性
-
保留非空特性:
大部分不会引入新元素的中间操作,如filter()、sorted()、distinct()等,会保留非空特性。 -
丧失非空特性:
若执行的操作可能引入null元素,如map()或flatMap(),则会移除该特性。
💡 示例:不同操作对非空特性的影响
Predicate<Stream<?>> isNonNull =
stream -> (stream.spliterator().characteristics() & Spliterator.NONNULL) != 0;
// 1️⃣ 来自 ArrayDeque —— 不允许 null
Stream<String> dequeStream = new ArrayDeque<>(List.of("A", "B", "C")).stream();
System.out.println("ArrayDeque: " + isNonNull.test(dequeStream)); // ✅ true
// 2️⃣ 来自 ConcurrentHashMap.newKeySet()
Stream<String> keySetStream = ConcurrentHashMap.<String>newKeySet().stream();
System.out.println("ConcurrentHashMap.newKeySet: " + isNonNull.test(keySetStream)); // ✅ true
// 3️⃣ 来自普通 Stream.of(...) —— 不保证 NONNULL
Stream<String> ofStream = Stream.of("apple", "banana", "cherry");
System.out.println("Stream.of(...): " + isNonNull.test(ofStream)); // ❌ false
// 4️⃣ 来自 Files.lines(...) —— 不包含 null
Path temp = Files.createTempFile("example", ".txt");
Files.write(temp, List.of("A", "B", "C"));
Stream<String> fileStream = Files.lines(temp);
System.out.println("Files.lines(...): " + isNonNull.test(fileStream)); // ✅ true
输出:
ArrayDeque: true
ConcurrentHashMap.newKeySet: true
Stream.of(...): false
Files.lines(...): true
🏁 总结
| 内容 | 说明 |
|---|---|
非空流(NONNULL)定义 |
流中不包含任何 null 元素 |
| 典型来源 | 不允许 null 的集合(如 ArrayDeque、ConcurrentHashMap.newKeySet())、Files.lines()、Pattern.splitAsStream()
|
| 保留特性操作 |
filter()、sorted()、distinct() 等 |
| 丧失特性操作 |
map()、flatMap() 等可能引入 null 的操作 |
| 判断方法 | 通过 stream.spliterator().characteristics() 检查是否包含 Spliterator.NONNULL
|
✅ 掌握非空流(NONNULL)的概念与特性,可以在数据处理时更安全、更高效地避免 null 带来的潜在问题,提高代码的健壮性与可读性。