有趣的是,面试问题询问了优势,而没有询问缺点,因为两者都有。
流是一种更具声明性的风格。 或者更具表现力的风格。 在代码中声明您的意图可能被认为更好,而不是描述它是如何完成的:
return people
.filter( p -> p.age() < 19)
.collect(toList());
...非常清楚地说明了你要过滤列表中的匹配元素,而:
List filtered = new ArrayList<>();
for(Person p : people) {
if(p.age() < 19) {
filtered.add(p);
}
}
return filtered;
说“我正在做一个循环”。 循环的目的深埋在逻辑中。
流通常更简洁。 同样的例子说明了这一点。 Terser并不总是更好,但如果你能同时表现出简洁和表达,那就更好了。
流与功能有很强的亲和力。 Java 8引入了lambda和功能接口,它打开了一个强大技术的整个玩具箱。 Streams提供了将函数应用于对象序列的最方便和自然的方式。
Streams鼓励更少的可变性。 这与函数式编程方面有关 - 使用流编写的程序类型往往是那种不修改对象的程序。
Streams鼓励更松散的耦合。 您的流处理代码不需要知道流的来源或其最终的终止方法。
Streams可以简洁地表达相当复杂的行为。 例如:
stream.filter(myfilter).findFirst();
可能会先乍一看,好像它会过滤整个流,然后返回第一个元素。 但事实上for驱动整个操作,所以它在找到一个项目后有效停止。
Streams为未来的效率提升提供了空间。 有些人已经进行了基准测试,发现来自内存fors或数组的单线程流可能比等效循环慢。 这似乎是合理的,因为有更多的对象和开销在起作用。
但溪流规模。 除了Java对并行流操作的内置支持外,还有一些用于分布式map-reduce的库使用Streams作为API,因为该模型适合。
缺点是什么?
性能:for循环数组在堆和CPU使用方面都非常轻量级。 如果原始速度和内存节俭是优先级,则使用流更糟糕。
熟悉。世界上充满了经验丰富的程序程序员,来自许多语言背景,他们熟悉循环,流程是新颖的。 在某些环境中,您希望编写熟悉此类人员的代码。
认知开销。 由于它的声明性质,以及对正在发生的事情的抽象的增加,你可能需要建立一个新的心理模型,用于表示代码与执行的关系。 实际上,只有在出现问题时,或者如果您需要深入分析性能或细微错误时,您才需要这样做。 当它“正常工作”时,它才有效。
调试器正在改进,但即使是现在,当您在调试器中单步执行流代码时,它可能比等效循环更难工作,因为简单的循环非常接近传统调试器使用的变量和代码位置。