在 Java 中,流相对于循环的优势是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44180101/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In Java, what are the advantages of streams over loops?
提问by user447607
I was asked this at an interview and I'm not convinced I gave the best answer I could have. I mentioned that you can do a parallel search and that null values were handled by some means I couldn't remember. Now I realize I was thinking of Optionals. What am I missing here? They claim it's better or more concise code but I'm not sure I agree.
我在一次采访中被问到这个问题,我不相信我给出了我能得到的最好答案。我提到您可以进行并行搜索,并且我不记得通过某种方式处理了空值。现在我意识到我在考虑 Optionals。我在这里缺少什么?他们声称这是更好或更简洁的代码,但我不确定我是否同意。
Considering how succinctly it was answered, it seems that this wasn't too broad a question after all.
考虑到它的回答是多么简洁,这似乎毕竟不是一个太宽泛的问题。
If they are asking this question at interviews, and clearly they are, what purpose could breaking it down serve other than to make it harder to find an answer? I mean, what are you looking for? I could break down the question and have all the sub-questions answered but then create a parent question with links to all the subquestions... seems pretty silly though. While we are at it, please give me an example of a less broad question. I know of no way to ask only part of this question and still get a meaningful answer. I could ask exactly the same question in a different way. For example, I could ask "What purpose do streams serve?" or "When would I use a stream instead of a for loop?" or "Why bother with streams instead of for loops?" These are all exactly the same question though.
如果他们在面试中问这个问题,而且很明显他们是,那么除了让他们更难找到答案之外,分解它还有什么目的?我的意思是,你在找什么?我可以分解问题并回答所有子问题,然后创建一个带有所有子问题链接的父问题......虽然看起来很愚蠢。在我们讨论这个问题的时候,请给我一个不那么广泛的问题的例子。我知道没有办法只问这个问题的一部分并且仍然得到有意义的答案。我可以用不同的方式问完全相同的问题。例如,我可以问“流服务的目的是什么?” 或“我什么时候会使用流而不是 for 循环?” 或者“为什么要使用流而不是 for 循环?” 这些都是完全相同的问题。
...or is it considered too broad because someone gave a really long multi-point answer? Frankly anyone in the know could do that with virtually any question. If you happen to be one of the authors of the JVM, for example, you could probably talk about for loops all day long when most of us couldn't.
...还是因为有人给出了一个很长的多点答案而被认为太宽泛了?坦率地说,任何知情人士几乎可以回答任何问题。例如,如果您碰巧是 JVM 的作者之一,您可能会整天谈论 for 循环,而我们大多数人都做不到。
"Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question."
“请编辑问题以将其限制为具有足够详细信息的特定问题,以确定适当的答案。避免一次提出多个不同的问题。请参阅如何提问页面以帮助澄清这个问题。”
As noted below, an adequate answer has been given which proves that there is one and that it is easy enough to provide.
如下所述,已经给出了一个充分的答案,证明有一个答案并且很容易提供。
采纳答案by slim
Interesting that the interview question asks about the advantages, without asking about disadvantages, for there are are both.
有趣的是面试问题问的是优点,而不是问缺点,因为两者都有。
Streams are a more declarative style. Or a more expressivestyle. It may be considered better to declare your intent in code, than to describe howit's done:
Streams 是一种更具声明性的风格。或者更具表现力的风格。在代码中声明你的意图可能被认为比描述它是如何完成的更好:
return people
.filter( p -> p.age() < 19)
.collect(toList());
... says quite clearly that you're filtering matching elements from a list, whereas:
... 很清楚地说明您正在从列表中过滤匹配元素,而:
List<Person> filtered = new ArrayList<>();
for(Person p : people) {
if(p.age() < 19) {
filtered.add(p);
}
}
return filtered;
Says "I'm doing a loop". The purpose of the loop is buried deeper in the logic.
说“我在做一个循环”。循环的目的更深地埋藏在逻辑中。
Streams are often terser. The same example shows this. Terser isn't always better, but if you can be terse and expressive at the same time, so much the better.
流通常更简洁。同一个例子说明了这一点。简洁并不总是更好,但如果您能同时简洁和富有表现力,那就更好了。
Streams have a strong affinity with functions. Java 8 introduces lambdas and functional interfaces, which opens a whole toybox of powerful techniques. Streams provide the most convenient and natural way to apply functions to sequences of objects.
流与函数有很强的亲和力。Java 8 引入了 lambda 表达式和函数式接口,这打开了一个包含强大技术的玩具箱。流提供了将函数应用于对象序列的最方便和自然的方式。
Streams encourage less mutability. This is sort of related to the functional programming aspect -- the kind of programs you write using streams tend to be the kind of programs where you don't modify objects.
流鼓励较少的可变性。这有点与函数式编程方面有关——您使用流编写的程序类型往往是您不修改对象的程序类型。
Streams encourage looser coupling. Your stream-handling code doesn't need to know the source of the stream, or its eventual terminating method.
流鼓励松散耦合。您的流处理代码不需要知道流的来源或其最终终止方法。
Streams can succinctly express quite sophisticated behaviour. For example:
流可以简洁地表达相当复杂的行为。例如:
stream.filter(myfilter).findFirst();
Might look at first glance as if it filters the whole stream, then returns the first element. But in fact findFirst()
drives the whole operation, so it efficiently stops after finding one item.
乍一看,好像它过滤了整个流,然后返回第一个元素。但实际上findFirst()
驱动整个操作,所以它在找到一个项目后有效地停止。
Streams provide scope for future efficiency gains. Some people have benchmarked and found that single-threaded streams from in-memory List
s or arrays can be slower than the equivalent loop. This is plausible because there are more objects and overheads in play.
流为未来的效率提升提供了空间。有些人进行了基准测试,发现来自 in-memory List
s 或数组的单线程流可能比等效循环慢。这是合理的,因为有更多的对象和开销在起作用。
But streams scale. As well as Java's built-in support for parallel stream operations, there are a few libraries for distributed map-reduce using Streams as the API, because the model fits.
但流规模。除了 Java 对并行流操作的内置支持外,还有一些使用 Streams 作为 API 的分布式 map-reduce 库,因为模型适合。
Disadvantages?
缺点?
Performance: A for
loop through an array is extremely lightweight both in terms of heap and CPU usage. If raw speed and memory thriftiness is a priority, using a stream is worse.
性能:for
通过数组的循环在堆和 CPU 使用方面都非常轻量级。如果原始速度和内存节俭是优先事项,则使用流会更糟。
Familiarity.The world is full of experienced procedural programmers, from many language backgrounds, for whom loops are familiar and streams are novel. In some environments, you want to write code that's familiar to that kind of person.
熟悉。世界上到处都是经验丰富的程序程序员,他们来自不同的语言背景,对他们来说循环是熟悉的,流是新奇的。在某些环境中,您希望编写这类人熟悉的代码。
Cognitive overhead. Because of its declarative nature, and increased abstraction from what's happening underneath, you may need to build a new mental model of how code relates to execution. Actually you only need to do this when things go wrong, or if you need to deeply analyse performance or subtle bugs. When it "just works", it just works.
认知开销。由于它的声明性质,以及对底层发生的事情的更多抽象,您可能需要构建一个新的代码与执行相关的思维模型。实际上,您只需要在出现问题时,或者需要深入分析性能或细微错误时才需要这样做。当它“正常工作”时,它就正常工作。
Debuggersare improving, but even now, when you're stepping through stream code in a debugger, it can be harder work than the equivalent loop, because a simple loop is very close to the variables and code locations that a traditional debugger works with.
调试器正在改进,但即使是现在,当您在调试器中单步执行流代码时,它也比等效循环更难工作,因为简单循环非常接近传统调试器使用的变量和代码位置。
回答by Alexey Romanov
You realized incorrectly: parallel operations use
Stream
s, notOptional
s.You can define methods working with streams: taking them as parameters, returning them, etc. You can't define a method which takes a loop as a parameter. This allows a complicated stream operation once and using it many times. Note that Java has a drawback here: your methods have to be called as
someMethod(stream)
as opposed to stream's ownstream.someMethod()
, so mixing them complicates reading: try seeing the order of operations inmyMethod2(myMethod(stream.transform(...)).filter(...))
Many other languages (C#, Kotlin, Scala, etc) allow some form of "extension methods".
Even when you only need sequential operations, and don't want to reuse them, so that you could use either streams or loops, simple operations on streams may correspond to quite complex changes in the loops.
您意识到错误:并行操作使用
Stream
s,而不是Optional
s。您可以定义处理流的方法:将它们作为参数,返回它们等。您不能定义将循环作为参数的方法。这允许一次复杂的流操作并多次使用它。请注意,Java 在这里有一个缺点:必须调用您的方法
someMethod(stream)
而不是流自己的方法stream.someMethod()
,因此将它们混合会使阅读变得复杂:尝试查看 中的操作顺序myMethod2(myMethod(stream.transform(...)).filter(...))
许多其他语言(C#、Kotlin、Scala 等)允许某种形式的“扩展方法”。
即使您只需要顺序操作,并且不想重用它们,以便您可以使用流或循环,对流的简单操作也可能对应于循环中相当复杂的变化。
回答by wero
You loop over a sequence (array, collection, input, ...) because you want to apply some function to the elements of the sequence.
你循环一个序列(数组、集合、输入……),因为你想对序列的元素应用一些函数。
Streams give you the ability to composefunctions on sequence elements and allow to implementmost common functions (e.g. mapping, filtering, finding, sorting, collecting, ...) independent of a concrete case.
流使您能够在序列元素上组合函数,并允许独立于具体情况实现最常见的函数(例如映射、过滤、查找、排序、收集等)。
Therefore given some looping task in most cases you can express it with less code using Streams, i.e. you gain readability.
因此,在大多数情况下,给定一些循环任务,您可以使用 Streams 用更少的代码来表达它,即您获得了可读性。
回答by VGR
Syntactic fun aside, Streams are designed to work with potentially infinitely large data sets, whereas arrays, Collections, and nearly every Java SE class which implements Iterable are entirely in memory.
除了语法上的乐趣之外,Streams 旨在处理潜在的无限大数据集,而数组、集合和几乎每个实现 Iterable 的 Java SE 类都完全在内存中。
A disadvantage of a Stream is that filters, mappings, etc., cannot throw checked exceptions. This makes a Stream a poor choice for, say, intermediate I/O operations.
Stream 的一个缺点是过滤器、映射等不能抛出已检查的异常。这使得 Stream 成为中间 I/O 操作的糟糕选择。
回答by Eugene
I'd say its parallelizationthat is so easy to use. Try iterating over millions of entries in parallel with a for loop. We go to many cpus, not faster; so the easier it is to run in parallel the better, and with Stream
s this is a breeze.
我会说它的并行化非常易于使用。尝试使用 for 循环并行迭代数百万个条目。我们去很多cpus,不是更快;所以并行运行越容易越好,并且使用Stream
s 是轻而易举的。
What I like a lot is the verbositythey offer. It takes little time to understand what they actually do and produce as opposed of howthey do it.
我最喜欢的是他们提供的冗长。了解他们实际做了什么和生产什么,而不是他们是如何做的,只需要很少的时间。