Java parallelStream最有效的列表类型是什么?
我有一个要处理的Java parallelStream最有效的列表类型是什么?,java,performance,list,java-8,java-stream,Java,Performance,List,Java 8,Java Stream,我有一个要处理的列表,我想进一步处理它 toProcess.parallelStream().map(/*some function*/).collect(Collectors.toList()); 对于初始列表,哪种列表类型(如LinkedList、ArrayList等)可以从多线程处理中获得最佳速度 其他信息:预期的元素计数范围为10^3-10^5,但单个元素可能会变得相当大(10^5-10^6个字符) 另外,我也可以到处使用String[],因为字符串的数量保证不会改变(结果将包含与t
列表
,我想进一步处理它
toProcess.parallelStream().map(/*some function*/).collect(Collectors.toList());
对于初始列表,哪种列表类型(如LinkedList、ArrayList等)可以从多线程处理中获得最佳速度
其他信息:预期的元素计数范围为10^3-10^5,但单个元素可能会变得相当大(10^5-10^6个字符)
另外,我也可以到处使用
String[]
,因为字符串的数量保证不会改变(结果将包含与toProcess一样多的元素)
无论哪种方式,我都必须在最后按顺序迭代所有元素。目前,我使用一个
foreach
-循环来组装最终结果。对于-循环,可以很容易地将其更改为常规的。考虑到上下文切换和一般多线程的成本。在列表类型之间切换的性能增益通常是微不足道的。即使你使用了一个次优的列表,也没关系
如果您真的关心,那么由于缓存位置的原因,ArrayList
可能会做得更好,但这要视情况而定。一般来说,ArrayList
比LinkedList
对并行化更友好,因为数组很容易分割成几块,交给每个线程
然而,由于您的终端操作是将结果写入文件,并行化可能对您毫无帮助,因为您可能会受到IO的限制,而不是CPU的限制 如果您确定输出元素的数量等于输入元素的数量,并且您对数组的结果感到满意,那么一定要使用toArray
而不是收集器。如果管道的大小是固定的,则目标阵列将以正确的大小进行预分配,并行操作将其结果直接存放到目标阵列的正确位置:不进行复制、重新分配或合并
如果需要列表
,则始终可以使用数组.asList
包装结果,但当然不能向结果中添加或删除元素
收集器
如果上述条件之一不成立,那么您需要与收集器打交道,后者有不同的权衡
收集器以线程限制的方式对中间结果进行操作,从而并行工作。然后将中间结果合并到最终结果中。有两种操作需要考虑:1)将单个元素累积到中间结果中,2)将中间结果合并(或组合)到最终结果中
在LinkedList
和ArrayList
之间,ArrayList
可能更快,但您可能应该对此进行基准测试。请注意,默认情况下,Collectors.toList
使用ArrayList
,尽管这可能会在将来的版本中更改
LinkedList
累积的每个元素(LinkedList.add
)都涉及分配一个新的列表节点并将其挂接到列表的末尾。将节点挂接到列表的速度相当快,但这涉及到对每个流元素的分配,随着累积的进行,这可能会导致较小的垃圾收集
合并(LinkedList.addAll
)也相当昂贵。第一步是将源列表转换为数组;这是通过在列表的每个节点上循环并将元素存储到临时数组中来实现的。然后,代码迭代这个临时数组,并将每个元素添加到目标列表的末尾。如上所述,这会为每个元素分配一个新节点。因此,合并操作非常昂贵,因为它在源列表中的每个元素上迭代两次,并对每个元素进行分配,这可能会引入垃圾收集开销
ArrayList
每个元素的累加通常涉及将其追加到ArrayList
中包含的数组的末尾。这通常非常快,但如果阵列已满,则必须重新分配该阵列并将其复制到更大的阵列中。ArrayList
的增长策略是将新数组分配为比当前数组大50%,因此重新分配与添加的元素数的对数成比例,这并不太糟糕。但是,必须复制所有元素,这意味着可能需要多次复制先前的元素
合并ArrayList
可能比LinkedList
便宜得多。将ArrayList
转换为数组需要将源中的元素批量复制(而不是一次一个)到临时数组中。如果需要(在本例中很可能是这样),目标阵列将调整大小,需要所有元素的大容量副本。然后将源元素从临时阵列大容量复制到目标阵列,目标阵列已预先调整大小以容纳它们
讨论
鉴于上述情况,似乎ArrayList
将比LinkedList
更快。然而,即使是收集到ArrayList
中,也需要对许多元素进行一些不必要的重新分配和复制,可能需要多次。未来的一个潜在优化是收集器.toList
将元素累积到一个数据结构中,该结构针对快速附加访问进行了优化,最好是预先调整大小以适应预期的元素数量。支持快速合并的数据结构也是一种可能
如果您所需要做的只是迭代最终结果,那么滚动您自己的具有这些属性的数据结构应该不会太困难。如果不需要一个完整的列表,那么可以进行显著的简化。它可以累积到预先确定大小的列表中,以避免真正的错误