Java Stream的distinct()默认基于equals()和hashCode()去重,仅对引用类型有效,需同时正确重写二者;否则去重失效;String等内置类型可直接使用,第三方类推荐用distinctByKey按字段去重。

Java Stream 的 distinct() 方法默认基于对象的 equals() 和 hashCode() 去重,但它**只对引用类型有效,且要求你自定义的类正确重写这两个方法**。否则,即使内容相同,也会被当作不同元素保留。
distinct 依赖 equals 和 hashCode 的原理
distinct() 内部使用了 LinkedHashSet 来记录已见过的元素。每次遇到新元素时,会调用其 hashCode() 定位桶位置,再用 equals() 判断是否真正重复。所以:
- 如果没重写
hashCode(),默认用Object.hashCode()(基于内存地址),两个内容相同的对象哈希值不同 → 不会被认为重复 - 如果只重写
equals()但没重写hashCode(),违反契约 → 可能导致HashSet查找失败,去重失效或行为不可预测 - 必须同时、正确地重写二者,且逻辑一致:
equal返回true的对象,hashCode必须返回相同值
正确重写 equals 和 hashCode 的示例
假设有一个 User 类,按 id 和 name 判定是否重复:
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) && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
这样写后,Stream 中的 distinct() 就能按业务语义去重了:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
List<User> users = Arrays.asList(
new User(1L, "Alice"),
new User(1L, "Alice"), // 与上一个相等
new User(2L, "Bob")
);
List<User> unique = users.stream().distinct().toList();
// 结果包含 2 个元素:[User(1,"Alice"), User(2,"Bob")]
不重写时的替代方案:用 distinctByKey
如果不能修改类(比如是第三方类),或只想按某几个字段去重,可以用自定义的无状态去重工具方法。常见写法是利用 ConcurrentHashMap 记录已见 key:
public static <T> Predicate<T> distinctByKey(Function<T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
// 使用示例:按 id 去重
List<User> uniqueById = users.stream()
.filter(distinctByKey(User::getId))
.toList();
这种方式灵活、安全,不依赖 equals/hashCode,适合临时性、字段级去重。
注意基本类型和 String 的特殊情况
String、包装类(Integer、Long 等)以及数组(需用 Arrays.equals())本身已正确实现 equals 和 hashCode,可直接用 distinct():
-
Stream.of("a", "b", "a").distinct().toList()→["a", "b"] -
Stream.of(1, 2, 1).distinct().toList()→[1, 2] - 但
int[]数组不行(Arrays.hashCode()需手动调用),建议转成List或用distinctByKey(Arrays::toString)

















