3 回答
TA贡献1821条经验 获得超6个赞
有趣的是,面试问题只问优点,而不问缺点,因为两者都有。
流是一种更具声明性的样式。或更富有表现力的风格。它可以被认为是更好的声明你的代码的意图,而不是描述如何,它的完成:
return people
.filter( p -> p.age() < 19)
.collect(toList());
...很清楚地说,您正在从列表中过滤匹配的元素,而:
List<Person> filtered = new ArrayList<>();
for(Person p : people) {
if(p.age() < 19) {
filtered.add(p);
}
}
return filtered;
说“我正在循环”。循环的目的被深深地埋在了逻辑中。
溪流通常更短。相同的示例显示了这一点。Terser并不总是更好,但是如果您可以同时简洁和富于表现力,那就更好了。
流与功能具有很强的亲和力。Java 8引入了lambda和功能接口,从而打开了包含强大技术的整个玩具箱。流提供了将函数应用于对象序列的最方便自然的方法。
流鼓励较少的可变性。这与功能编程方面有关-使用流编写的程序往往是不修改对象的程序。
溪流鼓励松散的耦合。您的流处理代码不需要知道流的源或其最终的终止方法。
流可以简洁地表达相当复杂的行为。例如:
stream.filter(myfilter).findFirst();
乍一看,好像它过滤了整个流,然后返回第一个元素。但是实际上findFirst()驱动了整个操作,因此在找到一项后它有效地停止了。
流为将来提高效率提供了空间。一些人进行了基准测试,发现内存中List或数组中的单线程流可能比等效循环慢。这是合理的,因为有更多的对象和开销在玩。
但是溪流规模。除了Java对并行流操作的内置支持之外,由于模型合适,因此有一些使用Streams作为API的分布式映射减少库。
劣势?
性能:for数组循环在堆和CPU使用率方面都非常轻巧。如果原始速度和内存节俭是优先考虑的事情,那么使用数据流会更糟。
熟悉。世界上到处都是经验丰富的程序程序员,来自许多语言背景,他们熟悉循环并且流是新颖的。在某些环境中,您想编写这种人熟悉的代码。
认知开销。由于它具有声明性,并且越来越多地从下面发生的事情中提取内容,因此您可能需要构建一个新的思维模型来了解代码与执行的关系。实际上,仅在出现问题或需要深入分析性能或细微的错误时才需要这样做。当它“起作用”时,它就起作用。
调试器正在改进,但是即使在现在,当您逐步调试调试器中的流代码时,它也比等效循环更难工作,因为简单的循环非常接近传统调试器所使用的变量和代码位置。
TA贡献1780条经验 获得超4个赞
您认识不正确:并行操作使用Streams而不是Optionals。
您可以定义使用流的方法:将它们作为参数,返回它们,等等。您无法定义将循环作为参数的方法。这允许一次复杂的流操作并多次使用。请注意,Java在此有一个缺点:方法的调用必须与someMethod(stream)stream的方法相反stream.someMethod(),因此将它们混合会使阅读变得复杂:尝试查看操作的顺序
myMethod2(myMethod(stream.transform(...)).filter(...))
许多其他语言(C#,Kotlin,Scala等)允许某种形式的“扩展方法”。
即使当您只需要顺序操作,又不想重复使用它们,以便可以使用流或循环时,对流的简单操作也可能对应于循环中相当复杂的更改。
添加回答
举报