在Java中,流比循环有什么优势?[关闭]


133

面试中有人问我这个问题,但我不相信我给出了我能提供的最佳答案。我提到您可以进行并行搜索,并且空值是通过某种我不记得的方式处理的。现在,我意识到我在考虑选修课。我在这里想念什么?他们声称这是更好或更简洁的代码,但我不确定我是否同意。


考虑到答案的简洁程度,看来这毕竟不是一个太宽泛的问题。


如果他们在面试中提出这个问题,而且很明显,将其分解除了能使找到答案变得更加困难之外,还有什么目的?我的意思是,你在找什么?我可以分解问题,让所有子问题都得到回答,但是然后创建一个带有所有子问题链接的父问题……虽然看起来很愚蠢。在讨论过程中,请举例说明一个不太广泛的问题。我不知道只问这个问题的一部分,仍然会得到有意义的答案。我可以用不同的方式问同样的问题。例如,我可以问“流的作用是什么?” 或“何时使用流而不是for循环?” 或“为什么要麻烦流而不是for循环?” 这些都是完全相同的问题。

...还是因为有人给出了很长的多点答案而被认为过于宽泛?坦率地说,任何有知识的人都可以解决任何问题。例如,如果您碰巧是JVM的作者之一,那么您可能可能整天都在谈论for循环,而我们大多数人却不能。

“请编辑问题以将其限制在一个特定的问题上,并要有足够的细节来确定适当的答案。避免一次提出多个不同的问题。请参阅“如何提出问题”页面,以帮助您弄清这个问题。”

如下所述,已经给出了足够的答案,证明存在一个答案,并且很容易提供。


7
这是基于恕我直言的。就个人而言,我更喜欢流,因为它使代码更具可读性。它可以让写什么你想要的,而不是如何。此外,单线执行令人惊奇的事情完全是一件坏事。
Arnaud Denoyelle

19
即使是30条线,一根班轮?我不喜欢长链。
user447607

1
此外,我在这里寻找的只是对采访的适当答复。这是唯一重要的“意见”。
user447607

1
从教育意义上讲,这个问题在以后的采访中也使我免于降级,@ slim确实钉住了它,但从工业上讲,它还谈到了Microsoft编程语言如何在抢夺Java语言的基础上发展自己的职业生涯,最后java通过抢夺Java语言使它报仇离开Lambda Expression并从对手那里流出,让我们看看Java在将来会如何处理Structs和Unions :)
ShayHaned

5
请注意,在函数式编程中,流仅消耗一部分功率:-/
ThorbjørnRavn Andersen

Answers:


251

有趣的是,面试问题只问优点,而不问缺点,因为两者都有。

流是一种更具声明性的样式。或更富有表现力的风格。它可以被认为是更好的声明你的代码的意图,而不是描述如何,它的完成:

 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使用率方面都非常轻巧。如果原始速度和存储节俭是优先考虑的事情,那么使用流会更糟。

熟悉。世界上到处都是经验丰富的程序程序员,来自许多语言背景,他们熟悉循环并且流是新颖的。在某些环境中,您想编写这种人熟悉的代码。

认知开销。由于它具有声明性,并且越来越多地从下面发生的事情中提取内容,因此您可能需要构建一个新的思维模型来了解代码与执行的关系。实际上,只有在出现问题或需要深入分析性能或细微的错误时才需要这样做。当它“起作用”时,它就起作用。

调试器正在改进,但是即使在现在,当您逐步调试调试器中的流代码时,它也比等效循环更难工作,因为简单的循环非常接近传统调试器所使用的变量和代码位置。


4
我认为可以很容易地整理一下,类似流的东西变得越来越普遍,并且现在以许多不是特别面向FP的常用语言出现。
Casey's

5
鉴于利弊在这里上市,我认为流是不值得比非常简单的用途以外的任何其他(小逻辑的if / then / else语句,没有多少嵌套调用或lambda表达式等),非critifcal性能部件
亨里克Kjus Alstad

1
@HenrikKjusAlstad绝对不是我打算交流的要点。流是成熟的,功能强大的,可表达的,并且完全适合于生产级代码。
苗条的

哦,我不是说我不会在生产中使用它。但是,我会默认使用老式的循环/ ifs等,而不是流,尤其是在生成的流看起来很复杂的情况下。我敢肯定,在某些情况下,流会打败循环,如果清楚的话,但通常,我认为它是“捆绑”的,有时甚至是相反的。因此,我会坚持坚持旧方法,而将注意力放在认知开销的论点上。
Henrik Kjus Alstad '18

1
@lijepdam-但您仍然会有代码“我正在对该列表进行迭代(请参见循环内部以查找原因)”,而“在列表上进行迭代”不是代码的核心意图。
苗条

16

除了语法上的乐趣,Streams旨在与可能无限大的数据集一起使用,而数组,Collections和几乎每个实现Iterable的Java SE类都完全在内存中。

Stream的一个缺点是过滤器,映射等无法引发检查的异常。对于中间I / O操作而言,这使Stream是一个糟糕的选择。


7
当然,您也可以遍历无限量的源。
苗条的

但是,如果要处理的元素保留在数据库中,那么如何使用流?初级开发人员可能会想要使用Streams来读取Collection中的所有内容。那将是一场灾难。
Lluis Martinez

2
@LluisMartinez一个好的数据库客户端库将返回类似的内容Stream<Row>,或者可以编写一个自己的Stream实现来包装数据库结果游标操作。
苗条的

Streams默默地忽略异常是我最近咬过的一个错误。不直观
xxfelixxx

@xxfelixxx流不会静默忽略异常。尝试运行此命令:Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
VGR

8
  1. 您认识不正确:并行操作使用Streams而不是Optionals。

  2. 您可以定义使用流的方法:将它们作为参数,返回它们,等等。您无法定义将循环作为参数的方法。这允许一次复杂的流操作并多次使用。请注意,Java在这里有一个缺点:方法的调用必须与someMethod(stream)stream的方法相反stream.someMethod(),因此将它们混合会使阅读变得复杂:尝试查看中的操作顺序

    myMethod2(myMethod(stream.transform(...)).filter(...))

    许多其他语言(C#,Kotlin,Scala等)允许某种形式的“扩展方法”。

  3. 即使当您只需要顺序操作,又不想重复使用它们,以便可以使用流或循环时,对流的简单操作也可能对应于循环中相当复杂的更改。


说明1. Optional接口不是链中处理null的方法吗?关于3,这是有道理的,因为对于短路滤波器,该方法仅在特定情况下被调用。高效。有道理,我可以说使用它们可以减少编写其他代码的需求,这些代码需要进行测试等。通过查看,我不确定您在2中的顺序情况是什么意思
。– user447607

1. Optional是的替代方案null,但与并行操作无关。除非您的问题中的“现在我意识到我正在考虑选配项”,否则只是在谈论null处理?
阿列克谢·罗曼诺夫

我更改了2和3的顺序,并将它们都扩大了一点。
阿列克谢·罗曼诺夫

6

您要遍历序列(数组,集合,输入等),因为您想对序列的元素应用某些功能。

流使您能够在序列元素上编写函数,并允许独立于具体情况实现最常用的函数(例如,映射,过滤,查找,排序,收集等)。

因此,在大多数情况下,如果执行某些循环任务,则可以使用Streams以更少的代码来表达它,即获得可读性


4
好吧,这不仅是可读性。您不必编写的代码就是您无需测试的代码。
user447607

3
似乎您也为面试提供了很好的答案
wero

6

我会说它的并行化是如此容易使用。尝试使用for循环并行遍历数百万个条目。我们去了很多,而不是更快。因此,并行运行越容易越好,使用Streams可以轻松实现。

我非常喜欢他们提供的详细信息。与他们如何做相比,花很少的时间来了解他们的实际生产和生产。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.