覆盖equals方法时总是要覆盖hashCode

什么时候需要覆盖equals方法?

当一个类有自己特有的“逻辑相等”概念时,需要重写equals()。

覆盖equals方法,必须遵守的通用约定

  • 自反性。对于任何非null的引用值x,x.equals(x)必须返回true
  • 对称性。对于任何非null的引用值x和y,当前仅当y.equals(x)返回true时,x.equals(y)必须返回true
  • 传递性。对于任何非null的引用值x和y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,x.equals(z)必须返回true
  • 一致性。多次调用x.equals(y)返回一致的结果
  • 非空性。对于任何非null的引用值x,x.equals(null)必须返回false

不要忽略这些约定,程序很有可能会运行不正常,直至崩溃。因为没有一个类是孤立的,类之间是相互传递调用的。例如集合类,依赖于传递给它的对象是否遵循equals约定。HashMap的get(key)方法,先是利用key的hash值找到数组的下标,如果发生hash冲突就是再利用equals进一步判断的。违反这些约定会导致,刚放入的一个键值对却发现无法通过键获取到值。

违反通用约定的结果

  • 自反性。你把一个对象放到集合中,之后去取的时候告诉你不存在该对象
  • 对称性。x.equals(y)为true,不同类的contain(x)和contain(y)返回的结果无法确定
  • 传递性。子类添加属性会影响到equals的比较结果,我们无法在扩展可实例化的类的同时,及增加类的值组件,同时保留equals约定,除非愿意放弃面向对象所带来的优势
  • 一致性。不要使equals方法依赖于不可靠的资源
  • 非空性。

实现高质量equals方法的诀窍

  • 使用==判断参数是否为这个对象的引用
  • 使用instanceof判断参数是否为正确的类型
  • 把参数转换为正确的类型
  • 对该类中的每一个关键域,判断是否和参数中对应的域相匹配(引用类型递归调用equals方法,除float和double的基本类型使用==)
  • 完成equals方法之后,确定是否满足5个通用约定

注意事项

  • 覆盖equals方法总要覆盖hashCode方法
  • 不要让equals方法过于智能
  • equals方法入参参数保持为Object

覆盖equals方法时总是要覆盖hashCode

很常见的错误来源就是没有覆盖hashCode方法。例如hashMap中会使用到散列码,put方法将key存储在一个散列桶中,两个相等的实例具有不同的散列码会导致get方法返回null。

覆盖hashCode方法,必须遵守的通用约定

  • 在应用程序执行期间,equals所用到的信息没有被修改的情况下,调用多次hashCode方法都返回同一个整数
  • 如果两个对象equals,那么这两个对象调用hashCode返回一样的整数结果。相等的对象必须具有相等的散列码
  • 如果两个对象不equals,那么这两个对象调用hashCode不要求返回一样的整数结果

为不相等的对象产生不同的散列码

  1. 把一个非零值,比如17,保存在一个int类型的result变量中。
  2. 对对象的每一个关键域,循环完成以下操作:
    1. 为该域计算int类型的散列码c
      • 如果该域类型为boolean,则计算(f?1:0)
      • byte、char、short、int,计算(int)f
      • long,计算(int)(f^(f>>>32))
      • float,计算Float.floatToIntBits(f)
      • double,计算Double.doubleToLongBits(f),然后再计算(int)(f^(f>>>32))
      • 引用类型,递归调用equals方法
      • 数组,需要把每一个元素当作独立的域来处理,可以使用Arrays.hashCode方法
    2. 计算 result = 31*result + c
  3. 返回result
  4. 确定是否满足“相等的对象具有相等的散列码”

为何选择31

  • 31是一个素数。如果乘数是偶数,乘法溢出的话信息会丢失。31*i 虚拟机可以优化为 (i<<5)-i,利用位移运算代替乘法可以提高性能。

只覆盖hashCode,不覆盖equals有什么影响?

哈希码的作用是为了提高哈希表的性能,让数据在哈希表中分布的更均匀。不覆盖equals方法不影响其他功能的使用,但是覆盖了equals一定要覆盖hashCode。
各位看官觉得呢?有问题请指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值