You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Stream中用filter+Set组合替代distinct获取唯一元素的性能探究

用ConcurrentHashMap替代Stream.distinct()的并行化与性能分析

核心逻辑差异

Stream自带的distinct()是有状态中间操作,并行执行时会先在每个线程本地维护独立的去重集合,最后再合并所有线程的结果做全局去重——整个过程由Stream框架自动处理,无需开发者操心线程安全。

而你用ConcurrentHashMap.newKeySet()配合filter(seen::add)的方式,是把去重逻辑绑定到了一个共享的线程安全集合上,每个元素都要调用线程安全的add()方法完成去重判断。

并行化表现:反而拉低效率

在并行流场景下,这种自定义方式的问题非常突出:

  • 每个线程处理元素时,都要对共享的ConcurrentHashMap执行原子操作。虽然ConcurrentHashMap用了分段锁优化高并发,但大量线程同时操作还是会引发锁竞争,导致线程等待,直接抵消并行带来的性能提升。
  • 原生distinct()通过本地先去重的策略,大幅降低了全局合并阶段的工作量,线程间协作成本更低,并行执行的效率反而更高。

性能对比:多数场景不如原生实现

  • 串行场景:原生distinct()使用普通HashSet(无线程安全开销),而ConcurrentHashMap的线程安全机制会带来额外性能损耗,所以串行下自定义方式的性能必然不如原生。
  • 并行场景:如上述所说,锁竞争会拖慢执行速度。只有在元素重复率极高、且线程数量极少的极端场景下,两者性能差距可能不大,但绝大多数业务场景中,原生distinct()的表现更优。

额外隐患:结果顺序不可控

如果原始流是有序的(比如ArrayList的流),原生distinct()在并行执行时会尽量保留元素的原始出现顺序。但用filter(seen::add)的方式,因为并行线程调度的不确定性,最终收集到的List元素顺序可能和原始流完全不一致,容易引入意料之外的逻辑bug。

总结

不推荐用这种方式替代distinct()。无论是并行化效率还是整体性能,原生distinct()都更有优势,而且框架已经封装好了线程安全、结果合并等细节,代码也更简洁易读。

内容的提问来源于stack exchange,提问作者Travis Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:22:31