java中stream.distinct()默认基于equals()和hashcode()去重,依赖linkedhashset实现;未重写二者则按引用比较,仅重写equals易导致哈希冲突;正确重写可按业务字段去重,也可用tomap或set辅助实现定制化去重。

Java 中 Stream.distinct() 默认就是根据元素的 equals() 和 hashCode() 方法去重的,前提是你的对象正确重写了这两个方法。
distinct 的默认行为依赖 equals + hashCode
distinct() 内部使用的是 LinkedHashSet 来记录已见元素,而 Set 判断是否重复,正是调用 equals()(配合 hashCode() 快速筛选)。所以只要你的类重写了 equals() 和 hashCode(),且逻辑合理,distinct() 就能按你期望的方式去重。
- 没重写
equals/hashCode→ 默认用Object版本 → 比较引用地址 → 所有对象都不同 - 只重写
equals不重写hashCode→ 可能违反哈希约定 → 去重失效或行为不可预测 - 正确重写两者 → 按业务字段判断相等性 →
distinct()正常工作
示例:自定义对象正确去重
比如有一个 User 类,想按 id 去重:
public class User {
private Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id); // 只看 id 是否相同
}
@Override
public int hashCode() {
return Objects.hash(id); // 必须和 equals 保持一致
}
}
然后这样用:
List<user> users = Arrays.asList(
new User(1L, "Alice"),
new User(2L, "Bob"),
new User(1L, "Alice2") // id 相同,会被去重
);
List<user> unique = users.stream().distinct().toList(); // 结果只有两个元素</user></user>
如果不想改对象,也可以用自定义去重方式
有时无法修改原类(比如第三方类),或者只想按某几个字段去重,可以用 Collectors.toMap 或辅助 Set 实现:
- 用
toMap按 key 去重(推荐):
List<user> unique = users.stream()
.collect(Collectors.toMap(
u -> u.getId(), // 去重依据的 key
Function.identity(), // value 就是原对象
(u1, u2) -> u1 // 冲突时保留第一个
))
.values()
.stream()
.toList();</user>
- 用外部
Set记录已见 key(适合流式处理大集合):
Set<long> seenIds = ConcurrentHashMap.newKeySet();
List<user> unique = users.stream()
.filter(u -> seenIds.add(u.getId())) // add 返回 true 表示首次加入
.toList();</user></long>
注意点
distinct() 是有状态操作,需要遍历全部元素并缓存已见值,内存占用随唯一元素数量线性增长。大数据量时注意 GC 和内存压力。
它不保证原始顺序以外的任何顺序(实际保持 encounter order),但不会打乱输入顺序。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











