Emi*_*and 174 java java-8 java-stream
我有一个列表myListToParse,我想过滤元素并在每个元素上应用一个方法,并将结果添加到另一个列表中myFinalList.
使用Java 8,我注意到我可以通过两种不同的方式完成它.我想知道他们之间更有效的方式,并理解为什么一种方式比另一种更好.
我对任何有关第三种方式的建议持开放态度.
方法1:
myFinalList = new ArrayList<>();
myListToParse.stream()
.filter(elt -> elt != null)
.forEach(elt -> myFinalList.add(doSomething(elt)));
Run Code Online (Sandbox Code Playgroud)
方法2:
myFinalList = myListToParse.stream()
.filter(elt -> elt != null)
.map(elt -> doSomething(elt))
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
ass*_*ias 41
我同意现有的答案,第二种形式更好,因为它没有任何副作用,更容易并行化(只使用并行流).
性能方面,在您开始使用并行流之前,它们似乎是等效的.在这种情况下,地图将表现得更好.请参阅以下微基准测试结果:
Benchmark Mode Samples Score Error Units
SO28319064.forEach avgt 100 187.310 ± 1.768 ms/op
SO28319064.map avgt 100 189.180 ± 1.692 ms/op
SO28319064.mapWithParallelStream avgt 100 55,577 ± 0,782 ms/op
Run Code Online (Sandbox Code Playgroud)
你不能以相同的方式提升第一个例子,因为forEach是一个终端方法 - 它返回void - 所以你被迫使用有状态的lambda.但如果您使用并行流,这确实是一个坏主意.
最后请注意,您的第二个代码段可以使用方法引用和静态导入以更简洁的方式编写:
myFinalList = myListToParse.stream()
.filter(Objects::nonNull)
.map(this::doSomething)
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
使用流的主要好处之一是它提供了以声明方式处理数据的能力,即使用函数式编程.它还提供免费的多线程功能,这意味着无需编写任何额外的多线程代码来使您的流并发.
假设您正在探索这种编程风格的原因是您希望利用这些优势,那么您的第一个代码示例可能无法正常运行,因为该foreach方法被归类为终端(意味着它可以产生副作用).
从功能编程的角度来看,第二种方式是优选的,因为map函数可以接受无状态lambda函数.更明确地说,传递给map函数的lambda应该是
ArrayList),则该函数不应该改变流的源.第二种方法的另一个好处是如果流是并行的并且收集器是并发的和无序的,那么这些特性可以为还原操作提供有用的提示以同时进行收集.
如果您使用Eclipse Collections,则可以使用该collectIf()方法。
MutableList<Integer> source =
Lists.mutable.with(1, null, 2, null, 3, null, 4, null, 5);
MutableList<String> result = source.collectIf(Objects::nonNull, String::valueOf);
Assert.assertEquals(Lists.immutable.with("1", "2", "3", "4", "5"), result);
Run Code Online (Sandbox Code Playgroud)
它急切地求值并且应该比使用 Stream 快一点。
注意:我是 Eclipse Collections 的提交者。
| 归档时间: |
|
| 查看次数: |
138096 次 |
| 最近记录: |