200元素列表取唯一值:两种Stream操作的延迟与内存效率对比
嘿,这个问题问得挺实在的——处理重复元素时,选对方法确实能在性能上带来小惊喜。结合你说的「200个元素、仅20个唯一」的场景,咱们从延迟(执行时间)和内存消耗两方面拆解这两种写法的差异:
内存消耗对比
list.stream().collect(Collectors.toSet()):
默认底层用的是HashSet(除非你指定其他Set实现)。HashSet的结构是数组+链表/红黑树,它会在遍历列表的同时直接去重存储。对于你的场景,最终只需要存20个唯一元素,HashSet初始容量默认是16,超过负载因子(0.75)后会扩容到32,内存占用就是HashSet自身结构加上20个元素的引用空间,全程只维护这一个集合。list.stream().distinct().collect(Collectors.toList()):distinct()操作内部其实是靠LinkedHashSet来记录已出现的元素(目的是保证元素和原列表中第一次出现的顺序一致),之后还要把LinkedHashSet里的元素复制到List(比如默认的ArrayList)中。这意味着内存里会同时存在两个集合:一个存20个元素的LinkedHashSet,一个存同样20个元素的ArrayList。虽然元素是引用,但LinkedHashSet的链表节点会额外占用一点内存,整体内存开销比toSet()略高。
延迟(执行时间)对比
toSet()更直接高效:
它是一步到位的操作:遍历列表时直接将元素加入HashSet去重,最后返回Set。全程只遍历一次元素,没有额外的复制步骤。对于你的场景,200个元素遍历一次,加上20个元素的哈希检查,速度很快。distinct().toList()多了一次复制步骤:
首先要遍历200个元素,用LinkedHashSet去重;然后还要遍历这个只有20个元素的LinkedHashSet,把元素复制到List里。相当于两次遍历(虽然第二次遍历的元素很少)。另外,LinkedHashSet因为要维护顺序,插入时比HashSet多了一点链表维护的开销,不过这个开销在元素少的时候几乎可以忽略。
结合你的场景总结
- 如果不需要保留原列表的元素顺序,
toSet()在内存和速度上都更优,而且代码更简洁。 - 如果必须保留元素第一次出现的顺序,那
distinct().toList()是标准选择,虽然内存和速度略逊,但在20个唯一元素的场景下,差异非常小,几乎感知不到。
额外提醒:两种方法的性能都依赖于你的对象的hashCode()和equals()实现——如果这两个方法写得低效,那不管选哪种写法,性能都会打折扣。
内容的提问来源于stack exchange,提问作者Laxmikant

