zoukankan      html  css  js  c++  java
  • 为什么覆写equals()方法的时候总是要覆写hashcode()?

    要回答这个问题,我们应该先认识一下obj中的equals和hascode方法

    1.equals()方法在obj中定义如下:

    public boolean equals(Object obj) {
    return (this == obj);

    可以看到因为‘==’,所以是将两个对象的地址值进行比较(比较对象的引用是不是相同),但是在String和Integer等包装类中已经重写了equals()和hashcode()方法,比如在String类中:

    public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
    char v1[] = value;
    char v2[] = anotherString.value;
    int i = offset;
    int j = anotherString.offset;
    while (n-- != 0) {
        if (v1[i++] != v2[j++])
    return false;
    }
    return true;
        }
    }
    return false;

    可以看出这不是对象地址的比较了,而是内容的比较,依此类推,其他包装类也是这样的

    附加:重写equals()需要注意的地方:

    • 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
    • 反射性:x.equals(x)必须返回是“true”。
    • 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
    • 还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
    • 任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。

     2.hashcode()方法在obj中定义如下:

    public native int hashCode(); 

    本地方法,与机器有关,但是它是将一个对象的内部地址转化成一个整数来实现的,在String等包装类中,hascode()方法也被重写了,例如String的Hascode():

    public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

                for (int i = 0; i < len; i++) {
                    h = 31*h + val[off++];
                }
                hash = h;
            }
            return h;

    可以看到String的hashcode()与对象的内容有关了,与地址无关了

    简单总结一下:在obj中的equals()和hashcode()是原始的,没有被重写的,且二者都与对象的地址有关,在String等包装类中,equals()和hashcode()是被重写了的,与对象的内容有关

    3.先看一下JDK中对hashcode的常协规定:

    • 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
    • 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
    • 如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能

            总结一下:根据equals()对象相同的时候,hashcode()一定要相同,根据equals()对象不同的时候,hashcode()可以相同,可以不同

    好了,既然两个方法我们都了解的差不多了,那么”为什么覆写equals()方法的时候总是要覆写hashcode()“???

    首先,我认为hascode主要是为集合类服务的,下面以hashset为例子,那我们先来了解一下HashSet

    HashSet:不允许出现重复对象,元素存放位置不确定

    HashSet存放元素的原理:当向Hashset中增加元素的时候,首先计算元素的hashcode,根据hashcode得到一个位置来存放该元素,如果在该位置没有元素,那么集合认为该元素不存在与计划中,所以直接增加进去,如果该位置有一个元素,那么将要放进去的元素与该位置上面已经存在的元素进行equals比较,若返回flase,则集合认为该对象不在集合中,可加入,然后因为该位置已经存在元素,所以进行一次散列,得到一个新的地址,若得到的地址中还是存在元素,那么一直进行散列,直到找到的地址中没有元素为止,然后把需要增加的元素放入该地址中,若true,则集合认为该元素已经存在与集合中,不会将对象增加到集合中。

    现在我们对HashSet有了一定的了解,那么继续”为什么覆写equals()方法的时候总是要覆写hashcode()“这个问题

    假如我在构造一个类的时候,只重写了equals方法而没有重写hashcode()方法,在我不需要使用集合的时候可能看不出什么问题,但是一旦我需要使用集合,问题就大了,而且这个bug还很隐秘,举个栗子:

     1 import java.util.*; 
     2 public class HashSetTest 
     3 { 
     4    public static void main(String[] args) 
     5     { 
     6                  HashSet hs=new HashSet(); 
     7                  hs.add(new Student(1,"zhangsan")); 
     8                  hs.add(new Student(2,"lisi")); 
     9                  hs.add(new Student(3,"wangwu")); 
    10                  hs.add(new Student(1,"zhangsan")); 
    11   
    12                  Iterator it=hs.iterator(); 
    13                  while(it.hasNext()) 
    14                  { 
    15                         System.out.println(it.next()); 
    16                  } 
    17      } 
    18 } 
    19 class Student 
    20    { 
    21      int num; 
    22      String name; 
    23      Student(int num,String name) 
    24                 { 
    25                 this.num=num; 
    26                  this.name=name; 
    27                  } 
    28               public String toString() 
    29                 { 
    30                     return num+":"+name; 
    31                  } 
    32            }      

    输出结果:(结果输出序号无序的原因是Hashset无序储存)

    1:zhangsan

    1:zhangsan 

     3:wangwu 

     2:lisi 

    观察结果,我们使用的是HashSet,但是结果中出现了相同的元素,这是为什么呢?我们来观察一下,我只重写了equals(),重写的equals比较的是对象的内容,当有两个new Student(1,"zhangsan"))的时候,这是两个内容相同的不同地址的对象,使用equals比较的结果是True,当时我没有重写hashcode,而obj下的hashcode的取值与对象的地址有关,所以这两个对象的hashcode是不同的,因为他们的地址不同,所以在放入集合的时候,集合看到二者的hashcode不同,认为二者是两个不同的对象,所以集合中就有了两个重复的对象,分析完毕

    解决方法:重写hascode()方法,继续上面的栗子:

     1 class Student 
     2 { 
     3 int num; 
     4 String name; 
     5 Student(int num,String name) 
     6 { 
     7             this.num=num; 
     8             this.name=name; 
     9 } 
    10 public int hashCode() 
    11 { 
    12             return num*name.hashCode(); 
    13 } 
    14 public boolean equals(Object o) 
    15 { 
    16             Student s=(Student)o; 
    17             return num==s.num && name.equals(s.name); 
    18 } 
    19 public String toString() 
    20 { 
    21             return num+":"+name; 
    22 } 
    23 } 

    结果:(结果输出序号无序的原因是Hashset无序储存)

    1:zhangsan
    3:wangwu
    2:lisi 

    可以看到输出结果中已经没有重复的元素了,这是因为我重写了hascode()方法,使得hashcode的取值只与对象的内容有关,而与对象的地址无关,所以集合比较hascode的时候看到二者的hascode相等,会认为二者是相同的对象,所以重复的对象不会被放进集合中

    错误和不清楚的地方还请大家指出,我也是初学者,难免有错误的地方,欢迎大家一起讨论学习!!!!

    欢迎拍砖!!!!!

                                                                                                                                                                                                                                                                                                     --期末,下午,16:58

    心之所向,素履以往
  • 相关阅读:
    203. Remove Linked List Elements
    86. Partition List
    143. Reorder List
    876. Middle of the Linked List
    246. Strobogrammatic Number
    202. Happy Number
    数据类型转换
    表达式
    面向对象
    对齐
  • 原文地址:https://www.cnblogs.com/yinbiao/p/8110196.html
Copyright © 2011-2022 走看看