要让distinct正确去重,关键在于明确定义“重复”:java stream依赖对象的equals()和hashcode()一致实现,sql distinct则依据select列值组合是否完全相同;二者均不修改原数据,只返回唯一结果。

要让 distinct 正确去重,关键不是“流中变量”本身,而是对象的唯一性判定逻辑是否被正确定义——Java 的 distinct() 依赖 hashCode() 和 equals(),SQL 的 DISTINCT 依赖所选列值的组合是否完全一致。
Java Stream 中 distinct 的去重逻辑
Stream 的 distinct() 是有状态中间操作,内部用 HashSet 缓存已见元素。它对每个元素执行两步判定:
- 先调用
hashCode()快速分桶,若哈希值不同,直接视为不重复; - 若哈希值相同,再调用
equals()比较内容,仅当两者都返回true才算重复。
所以:如果对自定义对象(如 User)使用 distinct(),必须重写 equals() 和 hashCode(),且二者逻辑必须一致。例如按 id 判定唯一性,就只基于 id 实现这两个方法。
常见错误是只重写 equals() 而忽略 hashCode(),会导致 HashSet 行为异常——相同对象可能被当成两个不同元素保留下来。
SQL 中 DISTINCT 的唯一性判定规则
SQL 的 DISTINCT 不看对象,只看查询结果中“选定列的值组合”是否完全相同:
-
SELECT DISTINCT name FROM users;:只要name值相等,整行即视为重复; -
SELECT DISTINCT name, dept FROM users;:只有name和dept同时相等,才算重复; -
NULL在所有数据库中均被视为一个独立且相等的值,即多行NULL只保留一行。
注意:DISTINCT 作用于最终投影结果,与原始表结构无关。它不会改变数据,也不支持按某列“逻辑主键”去重(比如只用 id 去重但返回整行),这类需求需配合 GROUP BY 或窗口函数。
底层实现差异与使用提醒
Java Stream 的 distinct() 是内存中实时判重,顺序流用单个 HashSet,并行流则拆分 + 合并,仍能保证结果正确性但不保证顺序;SQL 的 DISTINCT 由数据库引擎执行,通常会触发排序或哈希聚合,大数据量时性能敏感,必要时可考虑加索引或改用 GROUP BY 配合 MIN(id) 等方式优化。
两者都不修改原数据,都返回新结果集;也都要求你明确“什么是重复”——前者靠代码契约(equals+hashCode),后者靠语义设计(选哪些列)。










