Stream 与 Optional
约 1068 字大约 4 分钟
布欧-Lewyon
2026-05-16
首页 › Java › Lambda 与函数式接口 › Stream 与 Optional
Stream API(Java 8+)提供声明式的数据处理方式:将数据集合视为一个流,通过链式调用中间操作(过滤、映射、排序)和终端操作(收集、聚合)来表达数据处理逻辑。
创建 Stream
// 从集合创建
List<String> list = List.of("a", "b", "c");
Stream<String> stream = list.stream();
// 从数组创建
Stream<Integer> intStream = Arrays.stream(new int[]{1, 2, 3});
// 直接创建
Stream<String> of = Stream.of("x", "y", "z");
Stream<Integer> infinite = Stream.iterate(0, n -> n + 1); // 无限流
// 从文件行
Stream<String> lines = Files.lines(Path.of("input.txt"));中间操作(返回新 Stream)
中间操作是惰性的——不触发实际计算,只构建操作流水线。终端操作执行时才会逐元素处理。
List<String> words = List.of("apple", "banana", "cherry", "date");
// filter:筛选
words.stream()
.filter(w -> w.length() > 4)
.collect(Collectors.toList()); // [apple, banana, cherry]
// map:转换
words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList()); // [APPLE, BANANA, CHERRY, DATE]
// flatMap:展平嵌套集合
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4));
nested.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList()); // [1, 2, 3, 4]
// distinct:去重
// sorted:排序
// limit:截取前 n 个
// skip:跳过前 n 个
// peek:调试用(消费每个元素但不改变流)终端操作(触发执行)
// collect:收集到集合(最常用)
List<String> result = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
String joined = stream.collect(Collectors.joining(", "));
// forEach:遍历
stream.forEach(System.out::println);
// count:计数
long count = stream.count();
// anyMatch / allMatch / noneMatch:条件检查
boolean hasLong = words.stream().anyMatch(w -> w.length() > 10);
// findFirst / findAny:查找
String first = words.stream().filter(w -> w.startsWith("a")).findFirst().orElse("");
// reduce:归约
int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum); // 15Stream 与集合的组合示例
List<String> words = List.of("apple", "banana", "cherry", "date", "elderberry");
// 筛选长度 > 4 的单词,转大写,按字母序排序,取前 3 个
List<String> result = words.stream()
.filter(w -> w.length() > 4) // 中间操作
.map(String::toUpperCase) // 中间操作
.sorted() // 中间操作
.limit(3) // 中间操作
.collect(Collectors.toList()); // 终端操作
System.out.println(result); // [APPLE, BANANA, CHERRY]注意事项
- Stream 只能消费一次:终端操作后 Stream 已关闭,再次使用抛
IllegalStateException。 - 并行流
parallelStream()不适合有状态或需保持顺序的操作。 - 避免过长的链式调用——拆成多个步骤并用有意义的变量名提升可读性。
Optional
Optional<T> 是一个容器对象,可能包含非 null 值或为空,用于替代可能为 null 的返回值,迫使调用者处理"值不存在"的情况。
创建 Optional
Optional<String> opt1 = Optional.of("hello"); // 值不能为 null
Optional<String> opt2 = Optional.ofNullable(someVar); // 可为 null
Optional<String> opt3 = Optional.empty(); // 空容器安全取值
// 推荐:orElse / orElseGet
String val = opt.orElse("默认值");
String val2 = opt.orElseGet(() -> computeDefault()); // 惰性求值
String val3 = opt.orElseThrow(() -> new NoSuchElementException("值缺失"));
// 不推荐:直接 get(值缺失时抛 NoSuchElementException)
String raw = opt.get();常用方法
opt.isPresent(); // 是否有值
opt.ifPresent(v -> System.out.println(v)); // 有值时消费
opt.filter(v -> v.length() > 3); // 条件过滤
opt.map(String::toUpperCase); // 值转换
opt.flatMap(v -> Optional.of(v.trim())); // 展平转换
// 结合流
List<String> names = List.of("Alice", null, "Bob", null);
List<String> nonNull = names.stream()
.flatMap(Optional::ofNullable)
.map(String::toUpperCase)
.collect(Collectors.toList()); // [ALICE, BOB]Optional 使用边界
- 适合:方法返回值可能为空时,用
Optional让调用者明确处理。 - 不适合:字段类型(序列化问题)、方法参数(增加复杂度)、集合中的元素(集合本身为空就够了)。
- Optional 不是一个通用的
null替代品——它的设计目的就是返回值。
小结
- Stream 管道:创建 → 0 个或多个中间操作(惰性)→ 1 个终端操作(触发执行)。
- 常用中间操作:
filter、map、flatMap、sorted、distinct、limit。 - 常用终端操作:
collect、forEach、count、reduce、anyMatch。 - Stream 只能消费一次;避免过长的链式调用。
Optional用于方法返回值,迫使调用者处理空值;orElse/orElseGet比get更安全。- 易错:
Optional.of(null)抛NullPointerException,见null时应使用Optional.ofNullable。orElse(default)不管值是否存在都会计算default,如果计算开销大或可能有副作用,用orElseGet惰性求值。 - 思考任务:有一个
List<String>包含若干姓名(含null),用 Stream 过滤掉空值和长度小于 2 的,剩余转大写并按字母序排序,收集为List。
上一节:Lambda 与函数式接口
下一节:面向对象基础
