Set.add(obj)不生效的根本原因是未重写equals()和hashCode()方法,导致相同字段的对象被视为不同实例;必须二者同时重写且逻辑一致,推荐用IDE生成或Lombok的@EqualsAndHashCode注解。

为什么直接往 Set 里加对象不生效
常见现象是:明明两个对象字段值一模一样,Set.add(obj) 却没去重,Set.size() 还在涨。根本原因是 Java 默认用 Object.hashCode() 和 equals() 判断是否重复,而你没重写这两个方法。哪怕 new User("a", 1) 和 new User("a", 1) 字段相同,只要类没重写 equals 和 hashCode,它们就是两个不同对象。
实操建议:
- 必须重写
equals和hashCode—— 缺一不可,且逻辑要一致(比如都只看name和age) - 用 IDE 自动生成(如 IntelliJ 的
Alt+Insert → equals() and hashCode()),别手写漏字段 - 如果用 Lombok,加
@EqualsAndHashCode注解,并指定include字段,避免把id或时间戳等动态字段卷进去
用 LinkedHashSet 保留插入顺序再转回 List
很多场景既要“去重”,又要“保持原始顺序”——比如从数据库查出一堆记录,有重复但得按查询顺序留第一个。这时候别用 HashSet,它不保证顺序;也别手动遍历 List + contains,性能差(O(n²))。
实操建议:
- 直接用
new LinkedHashSet(list)构造,它自动去重且记住首次添加的顺序 - 再转成
List:new ArrayList(new LinkedHashSet(originalList)) - 注意:这个操作是 O(n),比双重循环快得多;但会新建集合,内存多一份拷贝
- 如果原
List很大且只读,考虑用Stream.distinct()(需对象已重写equals/hashCode):list.stream().distinct().collect(Collectors.toList())
遇到 NullPointerException 或字段为 null 怎么办
重写 equals 时,如果某个字段可能为 null(比如 String nickName),直接用 a.nickName.equals(b.nickName) 会 NPE。同理,Objects.hash(name, nickName) 能安全处理 null,但自己写 hashCode 时若用 nickName.hashCode() 就崩了。
实操建议:
-
equals中用Objects.equals(a.nickName, b.nickName),它内部判空 -
hashCode中用Objects.hash(name, nickName),不用手动调field.hashCode() - 如果字段是基本类型包装类(如
Integer age),也要当心null,别直接拆箱比较
想按部分字段去重(比如只看 name,忽略 age)怎么办
标准 Set 去重依赖整个对象的 equals,没法临时指定“只按 name 比”。硬改 equals 又污染业务语义(User 本来该全字段比)。
实操建议:
- 写个工具方法,用
Map按 key 提取去重:list.stream().collect(Collectors.toMap(u -> u.getName(), u -> u, (a, b) -> a)).values() - 或者用
Collectors.toMap的 merge 函数控制留哪个(第一个还是最新一个) - 别用
TreeSet加自定义Comparator:它只影响排序,不改变equals逻辑,去重仍无效
equals/hashCode,或者重写了但字段选错、没处理 null。一旦涉及“按某几字段去重”,就别硬套 Set,用 Stream + toMap 更直白可控。

















