订单去重时明明用了 HashSet,结果还是出现重复数据,你会从 equals 和 hashCode 哪些地方排查?
我的判断
HashSet 仍有重复,通常不是集合失效,而是业务去重键没有被 equals 和 hashCode 一致、稳定地表达。
我会先拿两条“看起来重复”的订单打印真正参与比较的字段和运行时类型。常见情况是 equals 比较了订单号,但 hashCode 还带着数据库主键;或者用了 Lombok,却把更新时间这类会变化的字段也生成进去了。这样两个对象要么落在不同桶里,要么加入 Set 后哈希值发生变化,后续就再也找不到原来的位置。
修复时不会为了迁就 Set 随便改整个订单实体。我更倾向于明确业务键,例如:
record OrderDedupKey(long tenantId, String requestNo) {}
然后用 Set<OrderDedupKey> 或 Map<OrderDedupKey, Order> 去重。这样“什么叫同一笔订单”写在类型里,也不会受实体其他字段变化影响。
内存去重只能处理当前进程这一批数据。真正防止重复下单,数据库还需要
(tenant_id, request_no)唯一约束,消费重试时按唯一键查询已有结果。
容易答偏踩坑误区
- 只重写
equals,忘了同步重写hashCode。 相等对象必须产生相同哈希值。 - 把可变字段放进哈希计算。 对象加入 Set 后再改状态或时间,会破坏集合定位。
- 只靠 HashSet 保证业务幂等。 多实例、重启和消息重放都会绕过进程内集合。