Java Stream的distinct()通过equals()和hashCode()去重:基本类型直接使用;自定义对象需重写这两个方法,或用toMap按指定字段去重;注意其有状态、内存占用及顺序特性。

Java Stream 的 distinct() 方法用于去除流中重复的元素,它依赖元素的 equals() 和 hashCode() 方法来判断是否重复。 用法简单,但效果和注意事项取决于你处理的数据类型。
基本用法:对简单类型去重
对于 Integer、String 等已重写 equals/hashCode 的类型,直接调用 distinct() 即可:
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 4);
List<Integer> unique = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 结果:[1, 2, 3, 4]
String 同理,大小写敏感("a" 和 "A" 视为不同元素)。
自定义对象去重:必须重写 equals 和 hashCode
如果流中是自定义类(比如 User),distinct() 默认按引用比较,所有对象都会保留。必须在类中正确重写 equals() 和 hashCode():
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
- 只根据业务上“相等”的字段(如 id 或 name)来实现这两个方法
- IDE(如 IntelliJ)可自动生成,避免手写出错
- 若使用 Lombok,加 @EqualsAndHashCode 注解即可
// 正确示例:User 类重写了 equals/hashCode(基于 id)
List<User> users = Arrays.asList(
new User(1, "Alice"),
new User(2, "Bob"),
new User(1, "Alice") // 与第一个 id 相同 → 被去重
);
List<User> uniqueUsers = users.stream()
.distinct()
.collect(Collectors.toList()); // 最终只有两个 User 对象
按某个字段去重(不修改原类)
若不能改实体类,或只想按某字段(如 name)去重,可用 Collectors.toMap 或 LinkedHashMap 辅助:
- 用 toMap(keyMapper, valueMapper, mergeFunction) 以指定字段为 key,自动覆盖重复项
- 保持插入顺序:用 LinkedHashMap::new 作为 mapFactory
List<User> uniqueByName = users.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(User::getName, Function.identity(),
(u1, u2) -> u1, LinkedHashMap::new),
map -> new ArrayList<>(map.values())
));
这比手动写 for 循环 + Set 判断更函数式,也避免了修改原类的限制。
注意点:distinct 是有状态操作,性能与数据量相关
- 内部使用 HashSet 缓存已见元素,内存占用随唯一元素数增长
- 无序流(如普通 List.stream())去重后顺序不变;并行流不保证顺序
- 对 null 值支持良好 —— 多个 null 会被视为相同,只保留一个

















