我有一个列表myListToParse,我想在其中过滤元素并对每个元素应用一个方法,并将结果添加到另一个列表myFinalList中。
在Java 8中,我注意到我可以用两种不同的方式来做到这一点。我想知道他们之间更有效的方式,并了解为什么一种方式比另一种更好。
我愿意接受任何关于第三条路的建议。
方法1:
myFinalList = new ArrayList<>();
myListToParse.stream()
.filter(elt -> elt != null)
.forEach(elt -> myFinalList.add(doSomething(elt)));
方法2:
myFinalList = myListToParse.stream()
.filter(elt -> elt != null)
.map(elt -> doSomething(elt))
.collect(Collectors.toList());
我同意现有的答案,第二种形式更好,因为它没有任何副作用,更容易并行化(只需使用并行流)。
在性能方面,它们似乎是等效的,直到您开始使用并行流。在这种情况下,map会表现得更好。以下为微基准测试结果:
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
你不能以同样的方式提升第一个例子,因为forEach是一个终端方法——它返回void——所以你不得不使用一个有状态的lambda。但如果你使用并行流,这真的是一个坏主意。
最后注意,你的第二个代码片段可以使用方法引用和静态导入以一种更简洁的方式编写:
myFinalList = myListToParse.stream()
.filter(Objects::nonNull)
.map(this::doSomething)
.collect(toList());
如果使用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);
它会快速计算,应该比使用流快一点。
注意:我是Eclipse Collections的提交者。
可能是方法3。
我总是喜欢把逻辑分开。
Predicate<Long> greaterThan100 = new Predicate<Long>() {
@Override
public boolean test(Long currentParameter) {
return currentParameter > 100;
}
};
List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> resultList = sourceLongList.parallelStream().filter(greaterThan100).collect(Collectors.toList());