Java 8流列表收集器内存分配速度vs带预分配的循环

Tob*_*obi 7 java memory java-8 java-stream

我想知道如果终端操作是列表收集器,Java 8流如何处理内存分配.

例如,考虑一下

List<Integer> result = myList.stream().map(doWhatever).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

VS

List<Integer> result = new ArrayList<>(myList.size());
for(String s : myList) {
  result.add(doWhatever.apply(s));
}
Run Code Online (Sandbox Code Playgroud)

如果使用流,则不知道列表将增长多少,这意味着必须进行某种重新分配.这个假设是真的吗?

结果列表的类型是某种链表,因此对元素的访问速度比ArrayList慢吗?

如果我从一开始就知道结果列表的大小,我是不是应该使用带有列表收集器的流?

Nic*_*tto 8

在场景后面Collectors.toList()将允许使用默认构造函数将您生成的元素收集Stream到一个ArrayList创建的默认容量中,10因此在大小超过的情况下确实需要重新分配10.

如果你想使用不同List的实现,请使用toCollection(Supplier<C> collectionFactory)哪个更通用的收集器,允许提供目标的工厂Collection.

例如,如果要将元素收集到一个LinkedList代码中,则可以将代码重写为下一个:

List<Integer> result = myList.stream()
    .map(doWhatever)
    .collect(Collectors.toCollection(LinkedList::new));
Run Code Online (Sandbox Code Playgroud)

假设你想要ArrayList一个默认容量为100,收集器就是Collectors.toCollection(() -> new ArrayList<>(100)).

  • 您可能只是使用`LinkedList`作为如何创建特定类型集合的示例.但我要提醒读者不要使用`LinkedList`,希望它比附加到'ArrayList`更快; 它可能不会.另一件事就是基准...... (4认同)

Lou*_*man 7

Collectors.toList()未指定任何关于它的实现.如果你在意,请使用toCollection(ArrayList::new).

如果我从一开始就知道结果列表的大小,我是不是应该使用带有列表收集器的流?

不,继续使用它们.分配是便宜的,相对于简洁性的胜利,成本是最小的.预定列表通常是不成熟的优化.

  • 是.这个.+1.对于有关读者,请注意,即使重新分配和复制被考虑在内,向"ArrayList"添加N个元素仍然是O(N). (3认同)
  • 直接在Stream上有很多需求,比如`stream.toList()`.人们对`stream.collect(Collectors.toList())`抱怨很多.如果收集到列表的唯一方法是`stream.collect(Collectors.toCollection(ArrayList :: new))`,情况会更糟.正如您所指出的,`toList()`没有指定它返回一个`ArrayList`,但实际上它确实如此.我怀疑程序已经成长为依赖于此.希望它能够返回一个像"SpinedBuffer"这样的快速附加列表,但这可能是一个太多的行为不兼容. (2认同)
  • 静态导入是一种很好的风格,但我倾向于在示例代码中避免使用它,因为我无法分辨读者对API的熟悉程度.对于`toCollection(ArrayList :: new)`的推荐,以及你提到的其他几点,在某些方面值得进行更长时间的讨论. (2认同)