
本文详细阐述在java中如何利用`supplier`接口和`stream.of()`方法,从一组固定表达式创建惰性求值的流。通过将每个表达式封装为`supplier`实例,并构建`stream
在Java 8及更高版本中,Stream API为数据处理提供了强大而灵活的工具。然而,当我们需要从一系列固定表达式创建流时,常见的Stream.of(expression1(), expression2(), ...)方法会立即执行这些表达式并将其结果作为流的元素。对于计算成本高昂或资源密集型的表达式,这种立即求值的行为可能导致不必要的性能开销或资源浪费。为了实现惰性求值,即仅在需要时才执行表达式,我们可以巧妙地结合Supplier接口。
核心概念:Supplier与惰性求值
java.util.function.Supplier
构建惰性流:Stream
要从固定数量的表达式生成一个惰性流,我们不直接将表达式的结果放入流中,而是将封装了这些表达式的Supplier实例放入流中。Stream.of()方法可以接受任意类型的对象作为其元素,因此它可以接受Supplier实例。
假设我们有三个表达式expression1()、expression2()和expression3(),它们都返回MyClass类型的对象。我们可以这样构建一个Stream
立即学习“Java免费学习笔记(深入)”;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class LazyStreamExample {
// 假设MyClass及其表达式
static class MyClass {
private String id;
public MyClass(String id) {
this.id = id;
System.out.println("MyClass " + id + " created."); // 模拟耗时操作
}
public String getId() {
return id;
}
}
// 模拟耗时表达式
private static MyClass expression1() {
return new MyClass("One");
}
private static MyClass expression2() {
return new MyClass("Two");
}
private static MyClass expression3() {
return new MyClass("Three");
}
public static void main(String[] args) {
System.out.println("--- 开始构建惰性流 ---");
Stream> lazyStream = Stream.of(
() -> expression1(),
() -> expression2(),
() -> expression3()
);
System.out.println("--- 惰性流构建完成,表达式尚未执行 ---");
// 此时,控制台不会输出 "MyClass One created." 等信息,
// 因为expression1()等方法尚未被调用。
}
} 在上述代码中,当Stream.of()被调用时,它仅仅创建了三个Supplier对象,并把它们作为流的元素。expression1()、expression2()和expression3()这些方法本身并没有被执行,因此其内部的耗时操作(例如MyClass的构造函数)也未被触发。
延迟求值与处理
一旦我们有了Stream
import java.util.function.Supplier;
import java.util.stream.Stream;
public class LazyStreamExample {
// ... (MyClass 和 expression1/2/3 方法同上) ...
public static void main(String[] args) {
System.out.println("--- 开始构建惰性流 ---");
Stream> lazyStream = Stream.of(
() -> expression1(),
() -> expression2(),
() -> expression3()
);
System.out.println("--- 惰性流构建完成,表达式尚未执行 ---");
// 示例:查找第一个满足条件的MyClass对象
System.out.println("\n--- 开始处理惰性流 ---");
MyClass result = lazyStream.map(Supplier::get) // 在此处调用Supplier::get,触发表达式执行
.filter(myObj -> {
System.out.println("过滤对象: " + myObj.getId());
return myObj.getId().equals("Two"); // 假设条件
})
.findFirst()
.orElse(null); // 或者orElseThrow()
if (result != null) {
System.out.println("\n--- 找到结果: " + result.getId() + " ---");
} else {
System.out.println("\n--- 未找到满足条件的结果 ---");
}
System.out.println("\n--- 再次处理惰性流,验证惰性特性 ---");
// 注意:Stream是单次消费的,这里仅为演示,实际应用中需重新构建流
// 假设我们重新构建一个流用于演示
Stream> anotherLazyStream = Stream.of(
() -> expression1(),
() -> expression2(),
() -> expression3()
);
anotherLazyStream.map(Supplier::get)
.filter(myObj -> {
System.out.println("再次过滤对象: " + myObj.getId());
return myObj.getId().equals("Three");
})
.findFirst();
System.out.println("--- 再次处理完成 ---");
}
} 运行上述代码,你将观察到以下行为:
- 在lazyStream构建完成时,不会有任何MyClass created.的输出。
- 当调用lazyStream.map(Supplier::get)时,Supplier::get方法才会被调用,从而触发expression1()、expression2()等方法的执行。
- 由于filter和findFirst的短路特性,一旦找到满足条件的对象(例如"Two"),后续的Supplier(例如expression3())将不会被调用,其对应的MyClass也不会被创建。这充分体现了惰性求值的优势。
优势与注意事项
优势:
- 性能优化: 避免了不必要的计算。对于计算成本高昂的表达式,只有当其结果确实被需要时才执行,可以显著提升应用程序的性能。
- 资源节省: 如果表达式涉及文件I/O、网络请求或数据库查询等资源密集型操作,惰性求值可以确保这些资源仅在必要时才被占用和释放。
- 处理潜在错误: 如果某个表达式可能抛出异常,惰性求值可以延迟异常的发生,直到实际尝试获取其结果时。
注意事项:
- 适用于固定数量的表达式: 这种方法最适合于预先已知且数量固定的表达式集合。
-
与Stream.generate()的区别: 对于需要生成无限流或基于某种逻辑动态生成元素的场景,Stream.generate(Supplier
s)是更合适的选择。它每次请求元素时都会调用Supplier的get()方法。而Stream >是针对固定数量的“未来值”的流。 -
流的单次消费特性: Java的Stream是单次消费的。一旦一个流的终端操作被执行,该流就不能再被使用。如果需要多次处理相同的惰性表达式集合,你需要重新构建Stream
。
总结
通过将表达式封装在Supplier中并构建Stream










