为什么重写equals就需要重写hashCode
在阿里巴巴开发手册-华山版 中有一处强制规定:
【强制】关于 hashCode 和 equals 的处理,遵循如下规则:
1) 只要覆写 equals,就必须覆写 hashCode。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆
写这两个方法。
3) 如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。
说明:String 已覆写 hashCode 和 equals 方法,所以我们可以愉快地使用 String 对象作为 key 来使 用。
我们可以在Object类中hashCode方法上面的注释得到以下几点:
- 同一个对象反复调用hashCode方法,返回的结果都是一致的。(前提是这个对象没被修改过)
- 如果使用equals方法比较得到两个对象相等,那么这两个对象去调用hashCode方法返回的值是相等的
- 两个对象的hashCode值相等,两个对象不一定相等。
由此,我们可以得出结论:
- 如果两个对象equals完结果相同,那么hashCode值肯定相等。
- 如果两个对象的hashCode值不相等,那么这两个对象也不相同。
我们在来看看Object类中的equals方法,equals是用来判断两个对象是否相等的,从官方注释可以知道它具备的性质:
- 它是自反的:对于任何非空引用值x , x.equals(x)应该返回true 。
- 它是对称的:对于任何非空引用值x和y , x.equals(y)应返回true当且仅当y.equals(x)返回true 。
- 它是可传递的:对于任何非空引用值x 、 y和z ,如果x.equals(y)返回true并且y.equals(z)返回true ,那么x.equals(z)应该返回true 。
- 它是一致的:对于任何非空引用值x和y , x.equals(y)多次调用始终返回true或始终返回false ,前提是没有修改对象的equals比较中使用的信息。对于任何非空引用值x , x.equals(null)应返回false 。
在注释中,有一句特别注意的话就是,每当重写此方法时,通常都需要重写hashCode方法,以维护hashCode方法的一般约定,即相等的对象必须具有相等的哈希码。
其实到了这里我们应该也能够明白了,hashCode和equals这两个方法根本就是配套使用的。那么要如何理解重写equals方法就必须要重写hashCode方法呢?
我们从Object类中可以看到,equals方法其实就是直接使用==
进行比较的。所以当两个引用对象指向同一个在堆中被分配内存的实例时,则才会返回true,否则则返回false。
但是,Object类中的equals方法原本的功能在我们真实开发中是远远不够的,当两个对象引用的堆实例不同时,它就会认为这两个对象不相等;但是我们在真是开发中,我们认为两个对象是否相等,取决与其实例的属性是否相等。
举个栗子:
1 | java复制代码A a1 = new A("123"); |
上面这段代码,如果在没有重写equals方法的时候,得到的结果会是false。因为在堆中会是两个不同的实例,但是在真实开发中,我们想要比较的是属性内容是否相等,也就是说,此时a1和a2属性内容是一样的,所以a1.equals(a2)则需要返回true。这种情况其实也很常见,例如常见的String类,就是重写了equals方法,当内容相等则返回true。
那么又回到原来的问题,在hashCode方法的官方注释我们可以知道:如果两个对象equals完返回true,那么hashCode值肯定相等。所以,我们在重写equals的时候,则必须重写hashCode,保证其hashCode值相等,不然就违背了原来的原则了。
总之,只要重写了equals方法,那么就需要重写hashCode方法。我们看到Java中的String类,这个类在Java集合中用到很多,例如我们平时使用的HashMap/Set等等集合,都是经常性的使用String类型去当作key来进行使用的。
Ⅰ. 如果String没有重写equals,也就是继承了Object类的equals方法的话,则如果String类型为key的时候,相同的字符串,也可能会被当成两个不一样的key,从而难以辨别。
Ⅱ. 如果String重写了equals,没有重写hashCode。那么也会出现,同一个key出现在不同的位置,因为集合中常用哈希函数来计算key的位置,在没有重写hashCode的情况下,则同一个key会出现hashCode值不同而导致位置不同。
本文转载自: 掘金