equal()和hashcode()方法简析

由于equal()和hashcode()是object中的方法,所以每个类中都有这两个方法,但在实际应用中往往需要重写这两个方法。

重写equal()和hashcode()方法
假设场景:java中的Set集合的特性之一是会去除相同的元素。假设在Set集合中存学生对象,同一个人只能在Set集合中存一次,判断一个学生是否是同一个人的依据为:姓名相同,年龄相同,即为同一人。
编写一个Student类:

public class Student {
    private String name;
    private Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

       //todo 添加get/set 方法

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

编写一个测试类:

public class SetTest {

    /**
     * 假设一场景:判断一个学生是否是同一个人的依据为:姓名相同,年龄相同,即为同一人
     * @param args
     */
    public static void main(String [] args){

        Set set = new HashSet();
        set.add(new Student("xiaoming",22));
        set.add(new Student("liangliang",24));
        set.add(new Student("xiaoming",22));
        set.add(new Student("daming",23));
        set.add(new Student("xiaoming",23));

        for (Iterator it =set.iterator();it.hasNext();){
            System.out.println(it.next());
        }
    }
}

测试结果:
image
显然,结果中出现两个xiaoming,年龄都为22,应该判断为一个人。出现这个结果是因为new出的每一个对象,哈希值是不同的, 想要实现预定的需求,必然要重写equal方法和hashcode方法。
在student类中重写这两个方法:

@Override
    public int hashCode() {
        return this.name.hashCode()+age * 31;
    }
    @Override
    public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if(!(this instanceof Student)){
            throw  new ClassCastException("类型不一样");
        }
        Student stu = (Student) obj; 
        return this.age == stu.age && this.name == stu.name;
    }

重写equal方法和hashcode方法有多种,可以根据自己的实际需求来指定合适的重写方法。
测试结果:
image

实现所需要的结果。

思考:为什么要重写equal和hashcode方法,这两个方法的调用机制是什么?

java中引入hashcode的方法主要就是为了查找方便,如果是一个数组去实现,想要找到其中一个值,就需要一个一个去比较,在数据量很大的情况下,性能会特别差,用hashcode方法可以快速找到存储的位置。那是否只重写hashcode()方法就可以呢?答案是否定的。只覆写hashcode 的方法,还是得不到预期的结果,因为同名同年龄得到的哈希值是一样的,当hash值一样时,就会去比较equal方法,此时调用的是object中的equal()方法,比较的是地址,都是新new出对象,地址不一样,所以equal返回为false,判断出两个学生不是同一人,所以要同时覆写equal方法。

这两个调用机制是什么呢,是先调用equal()方法,还是先调用hashcode()方法,接下来做个小实验,在这两个方法中分别打印出调用的对象:


  @Override
    public int hashCode() {
        System.out.println(this.getName()+"调用hashcode方法:"+this.name.hashCode()+age * 31);
        return this.name.hashCode()+age * 31;
      //  return age ;
    }
    @Override
    public boolean equals(Object obj) {

        if(this == obj)
            return true;
        if(!(this instanceof Student)){
            throw  new ClassCastException("类型不一样");
        }
        Student stu = (Student) obj;
        System.out.println(this.getName()+"调用equal方法" +stu.getName());
        //此equal()方法为String中的方法
        return this.age == stu.age &&  this.name.equals( stu.name);
    }

测试结果:
image

所以,在比较是否为同一个人时,首先会调用它的hashcode()方法,只有当hashcode()值一样是才回去调用equal()方法。第二个xiaoming是先调用hashcode()方法,发现和第一个xiaoming的哈希值是星等的,才再去比较equal()方法。这种机制会减少很多比较,提升效率。

如果两个对象的hashcode值不同,则必是两个不同的对象;但,两个相同的对象所产生的hashcode值是可能一样的,称为哈希碰撞。

最后欣赏下IDEA自动生成的equal()和hashcode()方法:


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name) &&
                Objects.equals(age, student.age);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页