use*_*607 110 java loops java-8 java-stream
我在接受采访时被问到这个问题,我不相信我能给出最好的答案.我提到你可以进行并行搜索,并且通过某些我无法记住的方法处理空值.现在我意识到我在想Optionals.我在这里错过了什么?他们声称它的代码更好或更简洁,但我不确定我是否同意.
考虑到它的回答是多么简洁,看起来这毕竟不是一个问题.
如果他们在面试中提出这个问题,而且显然是这样,除了让更难找到答案之外,还有什么目的可以打破它?我的意思是,你在找什么?我可以打破这个问题并解决所有子问题但是然后创建一个包含所有子项的链接的父问题......虽然看起来很傻.虽然我们在这,但请举一个不太广泛的问题的例子.我知道没有办法只问这个问题的一部分,仍然得到一个有意义的答案.我可以用不同的方式问同一个问题.例如,我可以问"流服务的目的是什么?" 或"我什么时候使用流而不是for循环?" 或"为什么要打扰流而不是循环?" 这些都是完全相同的问题.
......还是被认为过于宽泛,因为有人给出了一个非常长的多点答案?坦率地说,知情人士几乎可以解决任何问题.例如,如果您恰好是JVM的作者之一,那么当我们大多数人不能这样做时,您可能会整天谈论for循环.
"请编辑问题,将其限制在具有足够详细信息的特定问题上,以确定适当的答案.避免同时询问多个不同的问题.请参阅"如何询问"页面以获取有关此问题的帮助.
如下所述,已经给出了足够的答案,证明存在一个并且很容易提供.
sli*_*lim 214
有趣的是,面试问题询问了优势,而没有询问缺点,因为两者都有.
流是一种更具声明性的风格.或者更具表现力的风格.在代码中声明您的意图可能被认为更好,而不是描述它是如何完成的:
return people
.filter( p -> p.age() < 19)
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
...非常清楚地说明了你要过滤列表中的匹配元素,而:
List<Person> filtered = new ArrayList<>();
for(Person p : people) {
if(p.age() < 19) {
filtered.add(p);
}
}
return filtered;
Run Code Online (Sandbox Code Playgroud)
说"我正在做一个循环".循环的目的深埋在逻辑中.
流通常更简洁.同样的例子说明了这一点.Terser并不总是更好,但如果你能同时表现出简洁和表达,那就更好了.
流与功能有很强的亲和力.Java 8引入了lambda和功能接口,它打开了一个强大技术的整个玩具箱.Streams提供了将函数应用于对象序列的最方便和自然的方式.
Streams鼓励更少的可变性.这与函数式编程方面有关 - 使用流编写的程序类型往往是那种不修改对象的程序.
Streams鼓励更松散的耦合.您的流处理代码不需要知道流的来源或其最终的终止方法.
Streams可以简洁地表达相当复杂的行为.例如:
stream.filter(myfilter).findFirst();
Run Code Online (Sandbox Code Playgroud)
可能会先乍一看,好像它会过滤整个流,然后返回第一个元素.但实际上findFirst()驱动了整个操作,因此在找到一个项目后它会有效地停止.
Streams为未来的效率提升提供了空间.有些人已经进行了基准测试,发现来自内存List或数组的单线程流可能比等效循环慢.这似乎是合理的,因为有更多的对象和开销在起作用.
但溪流规模.除了Java对并行流操作的内置支持外,还有一些用于分布式map-reduce的库使用Streams作为API,因为该模型适合.
缺点是什么?
性能:for在堆和CPU使用方面,通过数组的循环非常轻量级.如果原始速度和内存节俭是优先级,则使用流更糟糕.
熟悉.这个世界充满了经验丰富的程序程序员,来自许多语言背景,对于他们来说,循环是熟悉的,流程是新颖的.在某些环境中,您希望编写熟悉此类人员的代码.
认知开销.由于它的声明性质,以及对正在发生的事物的抽象的增加,你可能需要建立一个新的心理模型,用于表示代码与执行的关系.实际上,只有在出现问题时,或者如果您需要深入分析性能或细微错误时,您才需要这样做.当它"正常工作"时,它才有效.
调试器正在改进,但即使是现在,当您在调试器中单步执行流代码时,它可能比等效循环更难工作,因为简单的循环非常接近传统调试器使用的变量和代码位置.
VGR*_*VGR 12
除了语法乐趣之外,Streams设计用于处理可能无限大的数据集,而数组,集合以及几乎每个实现Iterable的Java SE类都完全在内存中.
Stream的一个缺点是过滤器,映射等不能抛出已检查的异常.这使得Stream成为中间I/O操作的不良选择.
你意识到错误:并行操作使用Streams,而不是Optionals.
您可以定义使用流的方法:将它们作为参数,返回它们等.您不能定义将循环作为参数的方法.这允许复杂的流操作一次并多次使用它.请注意,Java在这里有一个缺点:你的方法必须被调用someMethod(stream)而不是stream自己的方法stream.someMethod(),所以混合它们会使阅读变得复杂:尝试查看操作的顺序
myMethod2(myMethod(stream.transform(...)).filter(...))
Run Code Online (Sandbox Code Playgroud)
许多其他语言(C#,Kotlin,Scala等)允许某种形式的"扩展方法".
即使您只需要顺序操作,并且不想重复使用它们,以便可以使用流或循环,对流的简单操作可能对应于循环中非常复杂的更改.
你循环一个序列(数组、集合、输入……),因为你想对序列的元素应用一些函数。
流使您能够在序列元素上组合函数,并允许独立于具体案例实现最常见的函数(例如映射、过滤、查找、排序、收集等)。
因此,在大多数情况下,给定一些循环任务,您可以使用 Streams 用更少的代码来表达它,即您获得了可读性。
我会说它的并行化非常易于使用。尝试使用 for 循环并行迭代数百万个条目。我们去很多cpus,不是更快;所以并行运行越容易越好,并且使用Streams 是轻而易举的。
我最喜欢的是他们提供的冗长。了解他们实际做了什么和生产什么,而不是他们是如何做的,只需要很少的时间。