在Stream的.collect方法中指定集合容量是否能提升性能?从性能收益角度,是否应优先选择指定初始容量的ArrayList收集方式而非默认Collectors.toList()?
好问题!这两个点其实都绕不开ArrayList的扩容机制,咱们一步步拆解清楚:
1. 指定集合初始大小是否有助于提升性能?
答案是肯定的,尤其是在处理大数据量的时候。
咱们先回忆下ArrayList的工作原理:它底层是数组实现,默认初始容量是10。当元素数量超过当前容量时,它会自动扩容到原来的1.5倍,而扩容的核心操作是把原数组的所有元素复制到新的更大数组里——这个复制过程是有性能开销的,而且扩容次数越多,累计的开销就越大。
如果在collect时指定了和最终元素数量匹配的初始大小(比如你这里的previouslyDefinedListSize刚好等于原list的元素个数,因为是map每个元素生成新元素),ArrayList就不需要进行任何扩容操作,直接一次性分配足够的空间,避免了多次数组复制的开销,自然能提升性能。
当然,如果数据量很小(比如几十个元素),这种性能提升可能微乎其微,几乎感知不到;但当数据量达到几千、几万甚至更多时,节省的开销就会非常明显。
2. 是否应该优先使用指定初始大小的toCollection替代Collectors.toList()?
这得分场景来看:
优先用指定初始大小版本的场景
- 你明确知道最终集合的元素数量(比如这次的场景,
map操作不会增减元素数量,原list的大小就是目标集合的大小) - 你需要一个可变的ArrayList(比如后续还要对这个集合进行添加、删除操作)
- 处理的数据量较大,性能优化有实际意义
这种情况下,代码:
List<?> tableList = list.stream() .map(row -> getRowModified(row, activeLimits)) .collect(Collectors.toCollection(() -> new ArrayList<>(previouslyDefinedListSize)));
确实比Collectors.toList()更优,因为它避免了扩容开销,同时明确指定了返回ArrayList,行为更可控。
适合用Collectors.toList()的场景
- 你不知道最终元素的数量,或者数据量很小,性能差异可以忽略
- 你不需要可变集合(Java 9及以后,
Collectors.toList()会返回一个不可修改的List,这种实现通常比ArrayList更轻量) - 代码简洁性优先,不需要额外的初始化逻辑
另外要注意:Java 8中的Collectors.toList()返回的是普通的ArrayList(初始容量10),但Java 9及以后的版本,Collectors.toList()返回的是一个内部优化的不可修改List,如果你不需要可变集合,这个版本的性能其实也不错,而且代码更简洁。
总结下来:如果你的场景符合「已知元素数量+需要可变ArrayList+数据量大」,那优先用指定初始大小的toCollection;否则根据简洁性和需求选择toList()就好。
内容的提问来源于stack exchange,提问作者M. Merim

