假设我们有一个列表,想要选择所有满足属性的元素(比如说一些函数f)。有3种方式可以并行执行此过程。
一个:
listA.parallelStream.filter(element -> f(element))..collect(Collectors.toList());
两个:
listA.parallelStream.collect(Collectors.partitioningBy(element -> f(element))).get(true);
三个:
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
//separate the listA into several batches
for each batch {
Future<List<T>> result = executorService.submit(() -> {
// test the elements in this batch and return the validate element list
});
}
//merge the results from different threads.
假设测试功能是CPU密集型任务。我想知道哪种方法更有效。非常感谢。
答案 0 :(得分:2)
使用.filter(element -> f(element)).collect(Collectors.toList())
时,它将匹配的元素收集到List
中,而.collect(Collectors.partitioningBy(element -> f(element)))
会将 all 元素收集到两个列表中的任意一个中,您只需删除其中之一,然后仅通过.get(true)
检索匹配项列表即可。
很明显,第二个变体在最佳情况下只能与第一个相提并论。,即无论是否所有元素都与谓词匹配,或者当JVM的优化器能够删除冗余元素时,工作。在最坏的情况下,例如当没有元素匹配时,第二个变体收集所有元素的列表,然后将其放下,而第一个变体将不收集任何元素。
第三个变体不可比,因为您没有显示实际的实现,而只是一个草图。将假设的实现与实际的实现进行比较是没有意义的。您描述的逻辑与并行流实现的逻辑相同。因此,您只是在重新发明轮子。您可能会做一些比参考实现更好的事情,或者只是针对您的特定任务做得更好,但是您可能会忽略Stream API实现者在持续了几年的开发过程中已经考虑过的事情。 >
因此,我不会在您的第三个变体上下任何赌注。如果我们加上时间,则需要完成第三个变体的实施,这将永远比仅使用其他变体中的任何一个更有效率。
因此,第一个变体是最有效的变体,尤其是它也是最简单,最易读的,直接表达您意图的变体。