hashCode和equals方法的关系

/ 1评 / 0

为什么覆盖equals方法一定要覆盖hashCode方法?

两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?

不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

equals方法和hashCode方法如果不同时按你自己逻辑覆盖的话,HashMap就会出问题。比如你只覆盖了equals方法而没有覆盖hashCode方法,那么HashMap在第一步寻找链表的时候会出错,有同样值的两个对象Key1和Key2并不会指向同一个链表或桶,因为你没有提供自己的hashCode方法,那么就会使用Object的hashCode方法,该方法是根据内存地址来比较两个对象是否一致,由于Key1和Key2有不同的内存地址,所以会指向不同的链表,这样HashMap会认为key2不存在,虽然我们期望Key1和Key2是同一个对象;反之如果只覆盖了hashCode方法而没有覆盖equals方法,那么虽然第一步操作会使Key1和Key2找到同一个链表,但是由于equals没有覆盖,那么在遍历链表的元素时,key1.equals(key2)也会失败(事实上Object的equals方法也是比较内存地址),从而HashMap认为不存在Key2对象,这同样也是不正确的。

一、equals方法的作用

   1、默认情况(没有覆盖equals方法)下equals方法都是调用Object类的equals方法,而Object的equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。

2 、要是类中覆盖了equals方法,那么就要根据具体的代码来确定equals方法的作用了,覆盖后一般都是通过对象的内容是否相等来判断对象是否相等。

没有覆盖equals方法代码如下:

     //学生类  
    public class Student {  
        private int age;  
        private String name;  
          
        public Student() {  
        }  
        public Student(int age, String name) {  
            super();  
            this.age = age;  
            this.name = name;  
        }  
        public int getAge() {  
            return age;  
        }  
        public String getName() {  
            return name;  
        }  
        public void setAge(int age) {  
            this.age = age;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
    }  

测试:

     import java.util.HashSet;  
    import java.util.LinkedList;  
    import java.util.Set;  
      
      
    public class EqualsTest {  
        public static void main(String[] args) {  
            LinkedList<Student> list = new LinkedList<Student>();  
            Set<Student> set = new HashSet<Student>();  
            Student stu1  = new Student(3,"张三");  
            Student stu2  = new Student(3,"张三");  
            System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
            System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
            list.add(stu1);  
            list.add(stu2);  
            System.out.println("list size:"+ list.size());  
              
            set.add(stu1);  
            set.add(stu2);  
            System.out.println("set size:"+ set.size());  
        }  
      
    }  

运行结果: stu1 == stu2 : false
stu1.equals(stu2) :  false
list size:2
set size:2

结果分析:Student类没有覆盖equals方法,stu1调用equals方法实际上调用的是Object的equals方法。所以采用对象内存地址是否相等来判断对象是否相等。因为是两个新对象所以对象的内存地址不相等,所以stu1.equals(stu2) 是false。

3、我们覆盖一下equals方法(age和name属性),让Student类其通过判断对象的内容是否相等来确定对象是否相等。

覆盖后的Student类:

     //学生类  
    public class Student {  
        private int age;  
        private String name;  
          
        public Student() {  
        }  
        public Student(int age, String name) {  
            super();  
            this.age = age;  
            this.name = name;  
        }  
        public int getAge() {  
            return age;  
        }  
        public String getName() {  
            return name;  
        }  
        public void setAge(int age) {  
            this.age = age;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        @Override  
        public boolean equals(Object obj) {  
            if (this == obj)  
                return true;  
            if (obj == null)  
                return false;  
            if (getClass() != obj.getClass())  
                return false;  
            Student other = (Student) obj;  
            if (age != other.age)  
                return false;  
            if (name == null) {  
                if (other.name != null)  
                    return false;  
            } else if (!name.equals(other.name))  
                return false;  
            return true;  
        }  
          
    }  

运行结果:

stu1 == stu2 : false
stu1.equals(stu2) : true
list size:2
set size:2

结果分析:因为Student两个对象的age和name属性相等,而且又是通过覆盖equals方法来判断的,所示stu1.equals(stu2) 为true。注意以上几次测试list和set的size都是2

二、HashCode

4、通过以上的代码运行,我们知道equals方法已经生效。接下来我们在覆盖一下hashCode方法(通过age和name属性来生成hashcode)并不覆盖equals方法,其中Hash码是通过age和name生成的。

覆盖hashcode后的Student类:

     //学生类  
    public class Student {  
        private int age;  
        private String name;  
          
        public Student() {  
        }  
        public Student(int age, String name) {  
            super();  
            this.age = age;  
            this.name = name;  
        }  
        public int getAge() {  
            return age;  
        }  
        public String getName() {  
            return name;  
        }  
        public void setAge(int age) {  
            this.age = age;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        @Override  
        public int hashCode() {  
            final int prime = 31;  
            int result = 1;  
            result = prime * result + age;  
            result = prime * result + ((name == null) ? 0 : name.hashCode());  
            return result;  
        }     
    }  

运行结果:

stu1 == stu2 : false
stu1.equals(stu2) : false
list size:2
hashCode :775943
hashCode :775943
set size:2

结果分析:我们并没有覆盖equals方法只覆盖了hashCode方法,两个对象虽然hashCode一样,但在将stu1和stu2放入set集合时由于equals方法比较的两个对象是false,所以就没有在比较两个对象的hashcode值。

5、我们覆盖一下equals方法和hashCode方法。

Student代码如下:

     //学生类  
    public class Student {  
        private int age;  
        private String name;  
        public Student() {  
        }  
        public Student(int age, String name) {  
            super();  
            this.age = age;  
            this.name = name;  
        }  
        public int getAge() {  
            return age;  
        }  
        public String getName() {  
            return name;  
        }  
        public void setAge(int age) {  
            this.age = age;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        @Override  
        public int hashCode() {  
            final int prime = 31;  
            int result = 1;  
            result = prime * result + age;  
            result = prime * result + ((name == null) ? 0 : name.hashCode());  
            System.out.println("hashCode : "+ result);  
            return result;  
        }  
        @Override  
        public boolean equals(Object obj) {  
            if (this == obj)  
                return true;  
            if (obj == null)  
                return false;  
            if (getClass() != obj.getClass())  
                return false;  
            Student other = (Student) obj;  
            if (age != other.age)  
                return false;  
            if (name == null) {  
                if (other.name != null)  
                    return false;  
            } else if (!name.equals(other.name))  
                return false;  
            return true;  
        }  
          
    }  

运行结果:

stu1 == stu2 : false

stu1.equals(stu2) :true

list size:2

hashCode :775943

hashCode :775943

set size:1

结果分析:stu1和stu2通过equals方法比较相等,而且返回的hashCode值一样,所以放入set集合中时只放入了一个对象。

  1. 6666说道:

    测试

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注