java中distinct()去重依赖equals()和hashcode(),基础类型直接可用;自定义对象须重写二者(推荐lombok),不可仅重写equals();不支持lambda指定字段,可用tomap等替代;避免修改影响判重的字段。

Java 中 distinct() 是 Stream 最直接的去重方式,但它不是“按字段一键去重”,而是严格依赖对象的 equals() 和 hashCode() 方法。用对了很简单,用错了就完全失效。
基础类型和字符串直接可用
Integer、String 等 JDK 内置类型已正确实现 equals() 和 hashCode(),调用 distinct() 就能按值去重:
List<integer> nums = Arrays.asList(1, 2, 2, 3, 3);</integer>nums.stream().distinct().collect(Collectors.toList()); // [1, 2, 3]- 同理,
List<string></string>去重也无需额外操作
自定义对象必须重写 equals 和 hashCode
如果对象没重写这两个方法,distinct() 默认按内存地址比较,两个内容相同的对象也会被当作不同元素保留。
- 用 Lombok 的
@Data或@EqualsAndHashCode注解可自动生成(推荐) - 手动重写时,参与比较的字段必须一致:比如
User按id和name判重,那两个方法里就得都包含这两个字段 - 避免只重写
equals()却漏掉hashCode(),否则 HashSet 无法正确定位桶位置
不能直接按单个字段去重?有替代方案
distinct() 本身不支持传入 lambda 指定字段,但可通过以下方式间接实现:
-
提取关键字段转为流:如按
user.getName()去重,可先map(User::getName).distinct(),再关联原对象(需保证姓名唯一或做后续映射) -
用 Collectors.toMap 保留首次出现项:例如按
id去重,stream.collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a)),再取 values - 借助 TreeSet + 自定义 Comparator:适合需要同时去重+排序的场景
注意可变对象带来的陷阱
如果对象在流中被修改(比如在 map 或 forEach 里改了影响 hashCode() 的字段),会导致已加入 Set 的对象“找不到自己”,重复元素可能漏检。
- 处理过程中尽量避免修改参与判重的字段
- 优先使用不可变类(如 record)或构造后不再变更的对象
- 若必须修改,建议先去重,再统一处理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











