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 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
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")]
</user></user>
不重写时的替代方案:用 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();
</user></object></t></t></t>
这种方式灵活、安全,不依赖 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)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










