在Java中,流循环有什么优势?

时间:2017-05-25 12:00:39

标签: java loops java-8 java-stream

我在接受采访时被问及此事,我不相信我给出了最好的答案。我提到你可以进行并行搜索,并且通过一些我无法记住的方法处理空值。现在我意识到我在想Optionals。我在这里错过了什么?他们声称它的代码更好或更简洁,但我不确定我是否同意。

考虑到它的回答是多么简洁,看起来这毕竟不是一个太宽泛的问题。

如果他们在面试中提出这个问题,并且显然是这样的话,除了让更难找到答案之外,还有什么目的可以打破它?我的意思是,你在找什么?我可以打破这个问题并解决所有子问题但是然后创建一个包含所有子项的链接的父问题......虽然看起来很傻。虽然我们在这,但请举一个不太广泛的问题的例子。我知道没有办法只问这个问题的一部分,仍然得到一个有意义的答案。我可以用不同的方式问同一个问题。例如,我可以问"流服务的目的是什么?"或"我何时使用流而不是for循环?"或"为什么要打扰流而不是循环?"这些都是完全相同的问题。

......还是被认为过于广泛,因为有人给了一个很长的多点答案?坦率地说,知情人士几乎可以解决任何问题。例如,如果您恰好是JVM的作者之一,那么当我们大多数人无法进行循环时,您可能会整天谈论循环。

"请编辑问题以将其限制为具有足够详细信息的特定问题,以确定适当的答案。避免一次提出多个不同的问题。请参阅“如何询问”页面以获取有关此问题的帮助。"

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

5 个答案:

答案 0 :(得分:185)

有趣的是,面试问题询问了优势,而没有询问缺点,因为两者都有。

Streams是一种更具说明性的风格。或者更强烈的富有表现力的风格。在代码中声明您的意图可能被认为更好,而不是描述 如何完成:

 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 。同样的例子说明了这一点。 Terser并不总是更好,但如果你能同时表现出简洁和表达,那就更好了。

Streams与功能具有很强的亲和力。 Java 8引入了lambda和功能接口,它打开了一个强大技术的整个玩具箱。 Streams提供了将函数应用于对象序列的最方便和自然的方式。

Streams鼓励减少可变性。这与函数式编程方面有关 - 使用流编写的程序类型往往是那种不修改对象的程序。

Streams鼓励更松散的耦合。您的流处理代码不需要知道流的来源或其最终的终止方法。

Streams可以简洁地表达非常复杂的行为。例如:

 stream.filter(myfilter).findFirst();

可能会先乍一看,好像它会过滤整个流,然后返回第一个元素。但实际上findFirst()驱动整个操作,因此在找到一个项目后它会有效停止。

Streams为未来的效率提升提供了空间。有些人已经进行了基准测试,发现来自内存List或数组的单线程流可能比等效循环慢。这似乎是合理的,因为游戏中有更多的对象和开销。

但是溪流规模。除了Java对并行流操作的内置支持外,还有一些用于分布式map-reduce的库,使用Streams作为API,因为模型适合。

<强>缺点吗

性能:在堆和CPU使用方面,数组的for循环非常轻量级。如果优先考虑原始速度和内存节俭,则使用流更糟糕。

熟悉。这个世界充满了经验丰富的程序程序员,来自许多语言背景,他们熟悉循环,流程是新颖的。在某些环境中,您希望编写熟悉此类人员的代码。

认知开销。由于它的声明性质,以及对正在发生的事情的抽象的增加,你可能需要建立一个新的心理模型,用于表示代码与执行的关系。实际上,只有在出现问题时,或者如果您需要深入分析性能或细微错误时,您才需要这样做。当它“正常工作”时,它就会起作用。

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

答案 1 :(得分:9)

除了语法乐趣之外,Streams旨在处理可能无限大的数据集,而数组,集合以及几乎每个实现Iterable的Java SE类都完全在内存中。

Stream的一个缺点是过滤器,映射等不能抛出已检查的异常。这使得Stream成为中间I / O操作的不良选择。

答案 2 :(得分:8)

  1. 您意识不到:并行操作使用Stream,而不是Optional

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

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

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

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

答案 3 :(得分:4)

循环遍历序列(数组,集合,输入......),因为您想要将某些函数应用于序列的元素。

Streams使您能够在序列元素上撰写函数,并允许实现最常见的函数(例如,映射,过滤,查找,排序,收集,...... )独立于具体案例。

因此,在大多数情况下,给定一些循环任务,您可以使用Streams以较少的代码表达它,即您获得可读性

答案 4 :(得分:2)

我会说它的并行化非常容易使用。尝试使用for循环并行迭代数百万个条目。我们去了许多cpu,而不是更快;因此,并行运行越容易越好,使用Stream s这是一件轻而易举的事。

我非常喜欢他们提供的详细程度。只需要很少的时间来理解他们实际做什么和生成什么,而不是他们是如何