Java高效编程(11):重写equals时必须同时重写hashCode

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界

每当你在类中重写 equals 方法时,必须同时重写 hashCode。如果不这样做,你的类将违反 hashCode 的通用合同,导致在使用 HashMapHashSet 等基于哈希的集合时出现问题。hashCode 方法合同的主要要求如下:

  1. 在应用程序执行期间,每次调用对象的 hashCode 方法时,如果对象未发生改变,应返回相同的值。该值不必在不同应用程序执行之间保持一致。
  2. 如果两个对象根据 equals 方法相等,则它们的 hashCode 必须相同。
  3. 如果两个对象根据 equals 方法不相等,它们的 hashCode 不必不同,但生成不同 hashCode 值可以提升哈希表的性能。

为什么需要重写 hashCode

假设我们有一个 PhoneNumber 类(如【条目10】所示)用作 HashMap 的键。如果该类只重写了 equals,而没有重写 hashCode,则即使两个 PhoneNumber 实例在逻辑上相等,它们的哈希值也会不同,这违反了 hashCode 合同。由于哈希值不同,HashMap 在尝试检索时可能会在错误的哈希桶中查找,导致 get 方法返回 null,即便插入时相等的实例的键值对确实存在。

为了解决这个问题,我们需要为 PhoneNumber 类编写一个合适的 hashCode 方法。以下是一个简单的例子:

// 合适的 hashCode 方法
@Override public int hashCode() {
    int result = Short.hashCode(areaCode);
    result = 31 * result + Short.hashCode(prefix);
    result = 31 * result + Short.hashCode(lineNum);
    return result;
}

编写一个好的 hashCode 方法

要编写一个好的 hashCode 方法,可以遵循以下步骤:

  1. 声明一个 int 类型的变量 result,并将第一个重要字段的 hashCode 赋值给它。
  2. 对于每个剩余的字段,计算其 hashCode 并将其加入 result 中:result = 31 * result + c;,其中 c 是该字段的 hashCode
  3. 返回 result

通过上述方法,hashCode 方法将确保逻辑上相等的对象有相同的哈希值,从而符合 hashCode 合同。

额外注意事项

  • 缓存 hashCode:如果计算 hashCode 的开销较大且类是不可变的,可以考虑在对象实例化时缓存 hashCode,以避免每次调用时重新计算。不过,需要确保在并发环境下缓存是线程安全的。

  • 不要排除重要字段:为了提升性能,不要排除在 equals 方法中被比较的字段,否则可能导致多个实例被映射到相同的哈希码,从而影响哈希表的性能。

  • 使用现有的工具:Java 提供了一些工具类,例如 Objects.hash(),可以简化 hashCode 方法的编写,虽然这种方法的性能较差,尤其是涉及原始类型时需要进行装箱和拆箱操作。在性能不敏感的场景中可以使用这种简化的方式。

性能考虑

hashCode 方法的质量直接影响哈希表的性能。如果不同实例的 hashCode 过于相似,哈希表的性能会退化成链表的线性查找,严重影响效率。因此,虽然编写一个 hashCode 方法可能略显繁琐,但它对于保证哈希表的性能至关重要。

总结

每次重写 equals 方法时,必须同时重写 hashCode,否则程序中的基于哈希的集合将无法正常工作。hashCode 方法应遵循合同规定,确保逻辑上不相等的对象尽量产生不同的哈希值。遵循本文中提供的步骤和建议,可以确保你编写的 hashCode 方法高效且可靠。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值