Stream 与 Optional 增强#
版本信息
- 最低 JDK:9(
Stream.takeWhile/dropWhile、Optional.ifPresentOrElse/or/stream)与 11(Optional.isEmpty),随 JDK 9/11 标准库增强 - API 文档:Java 11
java.util.stream.Stream|java.util.Optional
为什么需要#
JDK 8 的 Stream 只能 filter(保留全部满足条件的),没法表达「取满足条件的前缀、一旦中断就停」(如读一段正数直到第一个负数)。Optional 也缺胳膊少腿:判空只有 isPresent(),写否定要 !opt.isPresent()(别扭);没有「有值/无值分别处理」的一体化方法,被迫写 if-else;没有「为空则给另一个 Optional」的链式默认值,只能 orElse(且 orElse 的实参总是被求值)。JDK 9/11 一次性补齐这些日常痛点。
语法#
examples/stream-optional/ 里真实可运行的源码(snippets 嵌入,与示例零漂移):
package com.javamodern.streamopt;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
// JDK 9/11 给 Stream 与 Optional 补的日常方法:
// Stream.takeWhile / dropWhile(JDK 9)
// Optional.isEmpty(JDK 11)、Optional.ifPresentOrElse / or / stream(JDK 9)
public class StreamOptionalDemo {
/** Stream.takeWhile(JDK 9):取「满足谓词的最长前缀」,遇到第一个不满足即停。 */
public static List<Integer> takeWhilePositive(List<Integer> nums) {
return nums.stream().takeWhile(n -> n > 0).collect(Collectors.toList()); // (1)
}
/** Stream.dropWhile(JDK 9):丢「满足谓词的最长前缀」,从第一个不满足处开始保留。 */
public static List<Integer> dropWhilePositive(List<Integer> nums) {
return nums.stream().dropWhile(n -> n > 0).collect(Collectors.toList());
}
/** Optional.isEmpty(JDK 11):与 isPresent 互补,判空更直观。 */
public static boolean isAbsent(Optional<String> opt) {
return opt.isEmpty();
}
/** Optional.ifPresentOrElse(JDK 9):有值/无值分别处理,免去外部 if-else。 */
public static String describe(Optional<String> opt) {
StringBuilder sb = new StringBuilder();
opt.ifPresentOrElse(v -> sb.append("有:").append(v), () -> sb.append("无"));
return sb.toString();
}
/** Optional.or(JDK 9):当前为空则提供另一个 Optional(链式默认值)。 */
public static String valueOr(Optional<String> opt, String fallback) {
return opt.or(() -> Optional.of(fallback)).get();
}
public static void main(String[] args) {
System.out.println("takeWhile(1,2,-1,3) = " + takeWhilePositive(List.of(1, 2, -1, 3)));
System.out.println("dropWhile(1,2,-1,3) = " + dropWhilePositive(List.of(1, 2, -1, 3)));
System.out.println("isEmpty(empty) = " + isAbsent(Optional.empty()));
System.out.println("describe(present) = " + describe(Optional.of("Java")));
System.out.println("describe(absent) = " + describe(Optional.empty()));
System.out.println("valueOr(empty,默认) = " + valueOr(Optional.empty(), "默认"));
}
}
- :material-lightbulb:
takeWhile/dropWhile按前缀谓词操作(遇第一个不满足即停,区别于filter的全量筛选);Optional.isEmpty()(JDK 11)与isPresent()互补;ifPresentOrElse/or让 Optional 的分支与默认值链式化。
运行 StreamOptionalDemo.main 的实测输出(JDK 11):
takeWhile(1,2,-1,3) = [1, 2]
dropWhile(1,2,-1,3) = [-1, 3]
isEmpty(empty) = true
describe(present) = 有:Java
describe(absent) = 无
valueOr(empty,默认) = 默认
与 JDK 8 旧写法对比#
// 取「正数前缀」要手写循环或借助 iterate/spliterator,很绕
List<Integer> prefix = new ArrayList<>();
for (int n : nums) { if (n > 0) prefix.add(n); else break; }
// Optional 否定判空:!
boolean absent = !opt.isPresent();
// 有/无值分支:显式 if-else
String d = opt.isPresent() ? "有:" + opt.get() : "无";
// 默认值:orElse 实参总被求值(即使 opt 有值)
String v = opt.orElse(loadExpensiveDefault());
List<Integer> prefix = nums.stream().takeWhile(n -> n > 0).collect(toList());
boolean absent = opt.isEmpty(); // JDK 11,更直观
opt.ifPresentOrElse(v -> ..., () -> ...); // 有/无值分别处理
String v = opt.or(() -> Optional.of(loadDefault())) // 惰性:仅空时才求值
.orElseThrow();
底层原理#
takeWhile/dropWhile是短路、有状态的中间操作:在有序流上扫描元素,一旦谓词「翻转」即停止处理后续(区别于filter会扫完全部)。在无序流上语义退化为「任一满足/不满足子集」。Optional.or(Supplier)的回退是惰性的——Supplier只在当前 Optional 为空时才调用;而老的orElse(value)的value是方法实参,无论是否为空都会先求值(这是「有副作用/昂贵默认值」的经典坑,要改用orElseGet(Supplier)或or)。
graph LR
S["Stream"] --> TW["takeWhile: 取满足谓词的前缀<br/>遇中断即停(短路)"]
S --> DW["dropWhile: 丢满足谓词的前缀"]
S -.filter 全量扫描(对比).- F["filter"]
O["Optional"] --> E["isEmpty(JDK 11)"]
O --> I["ifPresentOrElse:有/无值双分支"]
O --> R["or(Supplier):惰性回退"]
style TW fill:#bbdefb
style R fill:#c8e6c9
常见坑 / 最佳实践#
takeWhile/dropWhile是「前缀」操作,不是「筛选」:[1,2,-1,3].takeWhile(>0)得[1,2],不会把后面的3也捞回来——要全量匹配用filter。- 无序流语义不同:无序流上
takeWhile/dropWhile的结果是「某个」满足子集,行为不确定;要稳定结果先用顺序流。 isEmpty()(JDK 11)别和isPresent()混写:判否定优先isEmpty(),可读性更好;旧代码用!isPresent()的可顺手替换。orElsevsorElseGetvsor:回退值昂贵或有副作用时,用orElseGet(Supplier)或or(Supplier)——orElse(expensive)即使值存在也会算一遍。Optional.stream():把Optional<T>转成 0/1 元素的Stream<T>,便于flatMap链式过滤掉空值。
小结#
takeWhile/dropWhile 填上了 Stream「按前缀操作」的缺口,isEmpty/ifPresentOrElse/or 让 Optional 的判空、分支、默认值链式化且惰性——都是 JDK 9/11 里高频、低风险、即用即得的标准库改进。
参考#
- Java 11
StreamJavadoc(takeWhile、dropWhile、ofNullable) - Java 11
OptionalJavadoc(isEmpty、ifPresentOrElse、or、stream)