最后更新于2017年11月6日星期一21:35:56 GMT
概述
“我总是选择一个懒惰的人去做困难的工作。. 因为他会找到一个简单的方法。”——比尔·盖茨
惰性求值是 评估策略 延迟表达式的求值,直到需要它的值. 与此相反的是急切评估, 表达式一旦绑定到变量就会被评估.[维基百科]
像大多数命令式编程语言一样, Java急切地求值方法的参数, 但是,我们应该考虑一种可以提高性能的惰性替代方案, 例如,避免不必要的昂贵计算.
我们将通过一个示例看到实现利用惰性求值是多么容易 功能接口 和 Lambda表达式.
例子
让我们假设我们有以下昂贵的计算:
静态布尔计算(字符串str) {
系统.出.println(“执行...");
//这里的计算很昂贵
返回str.包含(a);
}
即时抓取
考虑下面的函数,它接受两个布尔值,如果两个值都为真,则返回" match ", 否则返回“不兼容”!”.
静态字符串eagerMatch(boolean b1, boolean b2) {
返回b1 && b2 ? match:不兼容!";
}
public static void main(String [] args) {
系统.出.打印(eagerMatch(计算(“bb”),计算(aa)));
}
运行该程序产生以下输出:
执行...
执行...
不兼容的!
懒惰的评价
让我们使用provider函数接口实现一个延迟版本.
从 Java规范、接口
基本上,它表示一个不接受参数并返回值的函数. 在这个例子中,我们使用了一个布尔值的供应商来创建一个与即时匹配等价的延迟匹配:
lazyMatch(供应商a,供应商b) {
返回一个.get () && b.get () ? match:不兼容!";
}
因为provider是一个函数式接口,所以它可以被用作lambda表达式的赋值:
public static void main(String [] args) {
系统.出.println(lazyMatch(() -> compute("bb"), () -> compute("aa")));
}
如果没有匹配,运行这个程序的输出:
执行...
不兼容的!
在运行这个例子时需要注意两点:
- 在调用函数方法get时执行Compute.
- && 操作员出现“短路”, 这意味着只有在需要时才计算第二个操作数.
惰性参数和操作数求值的组合允许该程序避免在compute(" aa ")中执行昂贵的操作。.
结论
我们已经看到将急切求值的方法转换为惰性求值的方法是多么简单. 尽管与急切调用相比,惰性调用更复杂一些, 性能的提高弥补了表面上的缺点. 话虽如此, 不要到处使用懒惰策略, 但一定要在性能有明显改善迹象的情况下使用它, i.e. :
- 避免不必要的计算.
- 日志记录 [跑龙套.日志记录.日志记录器].
- 生成一个无限的数据结构,它只会被使用到某个未知的极限.
下一篇博文将展示如何利用流的惰性特性,从命令式风格转变为声明式风格.