当前位置: 首页
编程语言
如何利用 Stream.distinct() 去除集合流中的重复元素

如何利用 Stream.distinct() 去除集合流中的重复元素

热心网友 时间:2026-05-04
转载

如何利用 Stream.distinct() 去除集合流中的重复元素

如何利用 Stream.distinct() 去除集合流中的重复元素

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

Stream.distinct() 依赖对象的 equals() 和 hashCode()

这里有个关键点需要先搞清楚:distinct() 并不是对对象进行深度的值比较。它的底层逻辑,其实是调用每个元素的 equals()hashCode() 方法来判断是否重复。这意味着,如果你传入的是自定义对象(比如一个 User 类),但没有重写这两个方法,那么即使两个对象的字段内容完全一样,也会被当作不同的元素保留下来。

  • 对于 StringInteger 这类 JDK 内置类型,它们已经默认实现了正确的逻辑,可以直接使用。
  • 对于自定义类,就必须手动重写 equals()hashCode(),而且两者的逻辑必须保持一致。
  • 如果只重写了 equals() 却忘了 hashCode()distinct() 的行为可能会失效,或者变得不稳定,这可是个经典的坑。

distinct() 是有状态操作,不能并行流里随意替换顺序

distinct() 在内部使用了一个 LinkedHashSet 来缓存已经遇到过的元素。这个机制带来了两个特点:一是它会保留第一次出现的元素,二是它会尽力维持原始的顺序。然而,一旦进入并行流的世界,情况就变了。所谓的“第一次出现”,完全取决于线程的调度顺序,最终结果的一致性也就无法保证了。

  • 串行流Stream.of("a", "b", "a").distinct().toList() 的结果总是确定的 ["a", "b"]
  • 并行流Stream.of("a", "b", "a").parallel().distinct().toList() 的结果就不确定了,可能是 ["a", "b"],也可能是 ["b", "a"]
  • 如果业务场景要求并行处理、去重并且还要保持稳定顺序,通常的解决方案是先调用 sorted() 排序,或者干脆不使用 distinct(),转而采用 Collectors.toCollection(LinkedHashSet::new) 这类收集器。

distinct() 不适用于按某个字段去重的场景

这是另一个常见的误解。假设你有一个 List,想根据 userId 字段来去重,distinct() 是做不到的——它只能判断整个对象是否相等,无法让你指定按哪个字段去重。

  • 错误写法users.stream().distinct()。这依赖的是整个 User 对象的 equals() 方法,往往不是业务真正需要的语义。
  • 正确思路:通常需要借助 Collectors.toMap()Collectors.collectingAndThen(),配合 TreeSetLinkedHashMap 来实现。
  • 常用替代方案users.stream().collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a)).values()。这个写法清晰表达了“以 id 为键,保留第一个遇到的元素”的意图。

性能和内存开销比想象中大

别小看 distinct() 的开销。因为它需要缓存所有已经遍历过的元素,在最坏的情况下(所有元素都不重复),其内存占用会与输入流的长度成正比。同时,每次遇到新元素都要去哈希表里查询一次,也有计算成本。

  • 面对大数据量(比如百万级别)时,这个缓存集合很容易引发内存溢出(OOM),或者显著拖慢整个处理流程的速度。
  • 如果去重只是为了统计数量,使用 stream.distinct().count() 不如 stream.collect(Collectors.toSet()).size() 来得清晰,而且后者对中间集合的生命周期有更明确的控制。
  • 如果上游数据已经是排序好的,其实可以手动编写逻辑来跳过相邻的重复项(类似于归并排序中的去重思路),从而完全避免额外的集合开销。

话说回来,在实际使用中,不能只满足于“代码能跑通”。重点要审视几个方面:对象是如何定义的、数据规模有多大、是否需要保留顺序、业务上是否真的需要整个对象级别的判重。这几个问题如果没理清楚,distinct() 很容易就会变成一个隐蔽的问题来源。

来源:https://www.php.cn/faq/2419148.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
Linux下C++如何处理多线程同步

Linux下C++如何处理多线程同步

Linux下C++多线程同步:从互斥锁到屏障的实战指南 在Linux平台上用C++搞多线程开发,线程同步是个绕不开的核心议题。处理不好,数据竞争、死锁这些“坑”随时可能出现。那么,有哪些趁手的同步工具可供选择呢?它们的典型用法又是怎样的? 下面,我们就来梳理几种C++标准库中常用的线程同步机制,并配

时间:2026-05-04 22:48
C++在Linux上如何进行文件操作

C++在Linux上如何进行文件操作

在Linux上使用C++进行文件操作 说到在Linux环境下用C++处理文件,这个标准库头文件绝对是你的首选工具箱。它封装了一套直观的输入输出流接口,让文件读写变得像控制台输入输出一样顺手。下面,咱们就通过几个典型的场景,来看看它的基本用法。 1 打开文件 操作文件的第一步,自然是打开它。这里用s

时间:2026-05-04 22:48
Linux C++如何提高代码执行效率

Linux C++如何提高代码执行效率

在Linux环境下提升C++代码执行效率:一份实战指南 在Linux平台上用C++开发高性能应用,效率是绕不开的核心议题。代码反赌不快,往往直接决定了系统的吞吐能力和响应速度。那么,如何才能让C++程序在Linux环境下“火力全开”呢?这需要我们从算法选择、代码编写、编译器调优,一直到系统资源管理,

时间:2026-05-04 22:47
C++ Linux系统中怎样调试程序

C++ Linux系统中怎样调试程序

在Linux系统中,有多种方法可以用来调试C++程序 对于在Linux环境下进行C++开发的工程师来说,调试是绕不开的一环。面对复杂的逻辑或隐秘的Bug,手头没有几件趁手的工具可不行。好在Linux生态提供了丰富且强大的调试选项,从经典的命令行工具到现代的集成环境,再到专门的内存和性能分析器,足以应

时间:2026-05-04 22:47
Debian系统下Go语言打包有哪些注意事项

Debian系统下Go语言打包有哪些注意事项

在Debian系统下使用Go语言进行打包时,需要注意以下几个方面 将Go应用打包部署到Debian系统,看似是常规操作,但其中有不少细节值得推敲。处理得当,部署过程行云流水;忽略某些环节,则可能遇到意想不到的麻烦。下面就来梳理一下整个流程中的关键点。 1 环境准备 万事开头难,打好基础是关键。 安

时间:2026-05-04 22:47
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程