自定义类参与哈希集合必须重写hashcode()且与equals()一致:相等对象哈希码必须相同;应仅基于equals中使用的不可变字段计算,推荐用objects.hash()实现,避免返回常量、部分字段或动态值。

自定义类要正确参与哈希集合(如 HashSet、HashMap 的 key)或哈希表操作,必须重写 hashCode() 方法,并且要与 equals() 保持一致:相等的对象必须有相同的哈希码。
核心原则:与 equals 逻辑严格匹配
如果两个对象调用 equals() 返回 true,它们的 hashCode() 必须返回相同整数。反之不强制(哈希冲突允许),但冲突越少,哈希结构性能越好。
- 只对参与
equals比较的字段计算哈希码,忽略无关字段 - 避免在
hashCode()中使用可变字段(如未被声明为final且后续会修改的属性),否则对象放入哈希集合后修改字段会导致无法找回 - 推荐用
Objects.hash(...)简化实现,它自动处理null并组合多个字段
标准实现模板(推荐用 Objects.hash)
以一个含 name(String)、age(int)、id(Long)的 User 类为例:
@Override
public int hashCode() {
return Objects.hash(name, age, id);
}
这行代码等价于手动计算:name == null ? 0 : name.hashCode() ^ (age * 31) ^ (id == null ? 0 : id.hashCode()),但更安全、简洁、不易出错。
手动实现要点(理解原理时参考)
若不用 Objects.hash,需按以下步骤构造:
- 初始化一个非零常量(常用 1 或 17)作为 hash 基础值
- 对每个关键字段:用
Objects.hashCode(field)获取其哈希码(自动处理null),再乘以质数(常用 31),累加到结果中 - 例如:
result = result * 31 + Objects.hashCode(name);
质数 31 被广泛采用,因其是奇数、便于 JVM 优化(*31 可转为位移减法),且能较好分散哈希值。
常见错误与规避
以下写法看似简单,实则危险:
-
直接返回常量(如
return 1;):所有实例哈希码相同,哈希结构退化为链表,性能极差 -
只基于部分字段(如只用
id却在equals中比较name和age):违反契约,导致HashMap查不到本该相等的对象 -
使用随机数、时间戳、内存地址(如
System.identityHashCode(this)):每次调用结果不同,彻底破坏哈希一致性
IDE(如 IntelliJ IDEA)和 Lombok 都支持自动生成符合规范的 hashCode 和 equals,建议优先使用,避免手误。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











