HashSet去重依赖自定义类的equals和hashCode方法,需仅基于业务字段(如orderNo)实现,用Objects.hash和Objects.equals确保空安全与一致性,避免使用可变字段或数据库ID等陷阱。

HashSet 本身不提供“按某个字段去重”的配置开关,它的唯一性完全由元素自身的 hashCode() 和 equals() 行为决定。要实现按特定业务属性(比如 id、phone、orderNo)的唯一性约束,关键是在自定义类中**精准控制这两个方法的逻辑**,让它们只依赖你关心的业务字段。
核心:用业务字段驱动 equals 和 hashCode
例如,一个订单类 Order 要求“同一 orderNo 只能存一次”,那就必须:
-
equals()只比较orderNo是否相等,其他字段(如状态、时间)不参与判断 -
hashCode()也只基于orderNo计算,不能包含可变或无关字段 - 如果
orderNo可能为null,需在两个方法中做空值安全处理
推荐写法:用 Objects 工具类简化且防错
避免手写易错的 null 判断和哈希计算,直接使用 JDK 提供的工具:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
hashCode()写成:return Objects.hash(orderNo); -
equals()写成:return Objects.equals(this.orderNo, other.orderNo); - 加上类型检查和 this 引用判断(IDE 自动生成时通常已包含)
必须避开的坑
这些看似细微的问题,会直接导致去重失效:
- 把数据库自增
id当作判等依据——新对象id是 0 或null,多个新订单就全被当成“同一个” - 在
hashCode()中用了new Date()或随机数——同一对象多次调用返回不同值,插入后可能再也contains()不到 - 字段参与了
equals()却没进hashCode()(比如只比name,但 hash 里漏了它)——违反契约,去重行为不可预测 - 用了可变字段(如
List<Item> items)参与判等——后续修改列表内容,对象哈希值改变,原位置找不到,新位置又没存,相当于“消失”
验证是否生效的三步检查
写完别急着上线,快速跑三行代码确认:
- 创建两个
orderNo相同的Order实例,打印obj.hashCode()→ 必须相等 - 调用
o1.equals(o2)→ 必须返回true - 把它们都
add进HashSet<Order>,检查size()→ 必须是 1

















