JAVA中重写equals()方法的同时要重写hashcode()方法分析

熊孩纸 阅读:214 2020-02-17 16:10:35 评论:0

一、public boolean equals(Object obj)  和 hashcode()方法是object对象中的方法。

二、equals与hashcode间的关系是这样的: 

1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;

2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)。

即:

1、当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode() 必须为true;

2、当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2) 必须为false。

三、为啥重写equals?

     如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址。

四、什么是hashcode?

     hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。由于为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数。一个很好的例子就是在集合中的使用,我们都知道java中的List集合是有序的,因此是可以重复的,而set集合是无序的,因此是不能重复的,那么怎么能保证不能被放入重复的元素呢,单靠equals方法一样比较的话,如果原来集合中以后又10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,这个效率可想而知,因此hashcode就应遇而生了,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,如果有那么就用equals比较,如果没有则直接插入,这样就大大减少了equals的使用次数,执行效率就大大提高了。继续上面的话题,为什么必须要重写hashcode方法,其实简单的说就是为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equals都是根据对象的特征进行重写的),但hashcode确实不相同的。

五、那么如何保证这一点?

      这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,导致混淆,因此,就也需要重写hashcode()。


在集合框架中的HashSet,HashTable和HashMap都使用哈希表的形式存储数据,而hashCode计算出来的哈希码便是它们的身份证。哈希码的存在便可以: 

1、快速定位对象,提高哈希表集合的性能。
2、只有当哈希表中对象的索引即hashCode和对象的属性即equals同时相等时,才能够判断两个对象相等。
3、从上面可以看出,哈希码主要是为哈希表服务的,其实如果不需要使用哈希表,也可以不重写hashCode。但是SUN公司应该是出于对程序扩展性的考虑(万一以后需要将对象放入哈希表集合中),才会规定重写equals的同时需要重写hashCode,以避免后续开发不必要的麻烦。

重写equals的重要性

Java语言规范要求equals需要具有如下的特性:

1.自反性:对于任何非空引用 x,x.equals() 应该返回 true。
2.对称性:对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 true,x.equals(y) 也应该返回 true。
3.传递性:对于任何引用 x、y 和 z,如果 x.equals(y)返回 true,y.equals(z) 也应返回同样的结果。
4.一致性:如果 x 和 y 引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果。
5.对于任意非空引用 x,x.equals(null) 应该返回 false。

在对象比较时,我们应该如何编写出一个符合特性的 equals 方法呢,《Core Java》中提出了如下建议:

1、显式参数命名为 otherObject,稍后将它转换成另一个叫做 other 的变量。

2、检测 this 与 otherObject 是否引用同一个对象:

if (this == otherObject) return true;

3、检测 otherObject 是否为 null,如果为 null,返回 false。进行非空校验是十分重要的。

4、比较 this 与 otherObject 是否属于同一个类。

  • 如果每个子类都重写了 equals,使用 getClass 检验:
if (getClass() != otherObject.getClass())  
    return false; 
  • 如果所有子类都使用同一个 equals,就用 instanceof 检验:
if (!(otherObject instanceof ClassName)) 
    return false;

5、将 otherObject 转换为相应的类型变量。

ClassName other = (ClassName) otherObject;

6、现在可以对所有需要比较的域进行比较了。

  • 基本类型使用 == 比较
  • 对象使用 equals 比较
  • 数组类型的域可以使用静态方法 Arrays.equals检测相应数组元素是否相等
  • 如果所有域匹配,则返回 true

注意:子类重写父类 equals 方法时,必须完全覆盖父类方法,不能因为类型错误或者其他原因定义了一个完全无关的方法。可以使用 @Override 注解对覆盖父类的方法进行标记,这样编译器便会检测到覆盖过程中的错误。

重写 hashcode 的注意事项

散列码(hash code)是由对象导出的一个整型值。散列码没有规律,在不同的对象中通过不同的算法生成,Java中生成 hashCode 的策略为(以下说明均摘自 Java API 8):

String 类的 hashCode 根据其字符串内容,使用算法计算后返回哈希码。 

Returns a hash code for this string. The hash code for a String object is computed as s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

Integer 类返回的哈希码为其包含的整数数值。

Returns: a hash code value for this object, equal to the primitive int value represented by this Integer object.

Object 类的 hashCode 返回对象的内存地址经过处理后的数值。

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.

在自己的类中想要重写 hashCode 的话一般怎么做呢?建议合理地组合实例域的散列码,让各个不同对象产生的散列码更加均匀。例如我们现在有一个 Cat 对象,它有 namesize 和 color 三个不同域,那么可以重写 hashCode 方法如下:

class Cat { 
    ...... 
    public int hashCode() { 
        //hashCode是可以返回负值的 
        return 6 * name.hashCode() 
            + 8 * new Double(size).hashCode() 
            + 10 * color.hashCode(); 
    } 
    ...... 
}

当然还有更好的做法,我们可以直接调用静态方法 Objects.hash 并提供多个参数。这个方法会对各个参数调用 Object.hashCode,并组合返回的散列码。故以上的方法可以缩写为:

public int hashCode() { 
    return Objects.hash(name, size, color); 
}

注意: equalshashCode的定义必须一致,两个对象equalstrue,就必须有相同的hashCode。例如:如果定义的equals比较的是小猫的 name,那么hashCode就需要散列该 name,而不是小猫的 color 或 size。

标签:java
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
关注我们

扫一扫关注我们,了解最新精彩内容