减少 hashCode 冲突的关键在于合理组合参与 equals 判断的所有字段,推荐使用 31 累乘法(如 result = 31 * result + fieldHash),起始值为 1,需处理 null 和数组等特殊情况,避免简单相加或错误乘子。

要让 hashCode 计算更少冲突,关键不是“加多少字段”,而是**怎么组合字段**——既要覆盖所有参与逻辑相等判断的字段,又要用合理方式混合它们的哈希值,避免信息丢失或低位坍塌。
必须参与计算的字段
只要你在 equals 方法里用到了某个字段做比较,这个字段就必须参与 hashCode 计算。否则会违反 equals-hashCode 合约:相等对象必须有相同哈希码。
- 基本类型(如
int,boolean):用对应包装类的hashCode(),比如Integer.hashCode(age)、Boolean.hashCode(active) - 引用类型(如
String name,User owner):直接调用name.hashCode()、owner.hashCode()(前提是owner类也正确实现了hashCode) - 数组字段(如
int[] scores):不能直接调用scores.hashCode()(那是内存地址),要用Arrays.hashCode(scores) - 可能为
null的字段:必须显式判空,例如(email == null ? 0 : email.hashCode())
推荐的组合方式:31 累乘法
这是 JDK 多数类和 Objects.hash(...) 的标准做法,兼顾离散性、性能和低位保留:
result = 31 * result + fieldHash;
以两个字段为例:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
立即学习“Java免费学习笔记(深入)”;
@Override
public int hashCode() {
int result = 1;
result = 31 * result + (name == null ? 0 : name.hashCode());
result = 31 * result + age;
result = 31 * result + (active ? 1 : 0);
return result;
}- 起始值用
1(不是0),避免首字段为 0 时整个结果被“归零” - 每次乘
31是因为:它是质数、奇数、且可被 JVM 优化为位移减法(31 * i == (i ),既减少冲突又快 - 字段顺序会影响结果,但只要保持与
equals中的判断顺序一致即可,无需刻意调换
需要避开的常见陷阱
这些写法看似简单,实则大幅增加冲突概率:
-
只用一个字段:比如仅返回
id或name.hashCode(),其他字段变化不影响哈希值 → 冲突激增 -
简单相加:如
name.hashCode() + age→ “张三”+25 和 “李四”+24 结果相同,交换律导致大量碰撞 - 用 32、64 等 2 的幂作乘子:相当于左移,低位全补 0,大量哈希值末几位为 0,模数组长度时集中在少数桶中
-
忽略 null 安全:对可能为
null的引用直接调.hashCode()→ 抛NullPointerException
验证是否合理的小技巧
不需要穷举测试,但可以快速检查三点:
- 修改任意一个参与
equals的字段,hashCode值是否大概率改变?(可用 IDE 自动生成并人工扫一眼) - 两个明显不同的对象(如
new Person("a", 1)和new Person("b", 2)),哈希值是否不等? - 使用
Objects.hash(name, age, active)替代手写,效果一致且更安全 —— 生产环境推荐直接用它

















