hashcode()和equals()的联系 (Java)

文章详细介绍了Java中hashCode()和equals()的关系及其在散列表场景下的应用。重写这两个方法对于优化散列表操作的效率至关重要。只重写equals()可能导致在散列表中出现重复项,而只重写hashCode()无法准确判断对象是否相等。因此,为了保证散列表的正确性,通常需要同时重写hashCode()和equals()。
摘要由CSDN通过智能技术生成

hashcode()和equals()的联系

当我们往Collection集合中添加 8大基本类型和String类型的时候,不需要重写hashCode()和equals()方法。因为任何对象都是Object类的子类,所以任何对象都拥有这个方法。

hashCode介绍

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。

equals介绍

equals它的作用也是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较两个对象的地址是否相同,价于“==”。同样的,equals()定义在JDK的Object.java中,这就意味着Java中的任何类都包含有equals()函数。

对于没有用到散列表的情况:

equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。

使用equal()是一定可靠的,为什么还要重写hashCode()呢?

重写hasCode()可以提高检测效率: 程序会先使用 hasCode()方法,hasCode()的结果不同,可以说明两个对象不同,hasCode()若结果相同,不能一定说明对象相同,还要通过比较equal()再判断。由于需要大量并且快速的对比的话如果都用equal()去做显然效率太低 ,所以重写了hasCode()可以大大提升效率。

这种大量的并且快速的对象对比一般使用的hash容器中,比如hashset,hashmap,hashtable等等,比如hashset里要求对象不能重复,则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先hashCode(),如果hashCode()相同,再用equal()验证,如果hashCode()都不同,则肯定不同,这样对比的效率就很高了。

public class Demo006ArrayList {
    public static void main(String[] args) {
        // List  ArrayList   LinkedList
        List<Student> stus = new ArrayList<>();
        Student s1 = new Student("李思",19, false);
        stus.add(s1);
        stus.add(new Student("张三",18, false));
        stus.add(new Student("宝玉",28, true));

        System.out.println("链表长度:"+stus.size());
        System.out.println("链表内容:"+stus);

        // 会先使用 hashCode() 方法,在hashCode 相同的基础上再通过equals 判断是否相同。
        // 两个判断都相同时,才认定元素相等,contains 返回 true
        //
        System.out.println("是否存在s1:"+stus.contains(s1));
        Student s2 = new Student("张三", 18, false);
        System.out.println("是否存在s2:"+stus.contains(s2));
        System.out.println("s1的hashcode:"+stus.get(1).hashCode());
        System.out.println("s1的hashcode:"+s2.hashCode());
    }
}
public class Student {
    private String name;
    private Integer age;
    private Boolean  gender;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Boolean getGender() {
        return gender;
    }

    public void setGender(Boolean gender) {
        this.gender = gender;
    }

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

    @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) &&
                Objects.equals(gender, student.gender);
    }

    /**
     * 写得和属性相关,尽可能让那些属性不同的对象 hash值不同
     * @return
     */

    @Override
    public int hashCode() {
//        return (name.hashCode()+age*2 + (gender?1:0))/3;
        return 1;
    }

}

上面重写了两个方法hashCode()和equal()。

假如没有重写,可以看到一个是true,一个是false,就会调用hashCode()和equal()就都是返回当前对象的地址,没有比较意义。

返回结果:
链表长度:3
链表内容:[Student{name=‘李思’, age=19, gender=false}, Student{name=‘张三’, age=18, gender=false}, Student{name=‘宝玉’, age=28, gender=true}]
是否存在s1:true
是否存在s2:false
s1的hashcode:1163157884
s1的hashcode:1956725890

只重写hasCode()不行,可以看到一个是true,一个是false,后面的equal()会调用父类的方法,效果和==一样,比较的是地址,不是我们想要的比较。

结果如下:
链表长度:3
链表内容:[Student{name=‘李思’, age=19, gender=false}, Student{name=‘张三’, age=18, gender=false}, Student{name=‘宝玉’, age=28, gender=true}]
是否存在s1:true
是否存在s2:false
s1的hashcode:12
s1的hashcode:12

只重写equal()可以,可以看到一个是true,另一个也是true,但效率低,再重写hashCode(),提高效率。
链表长度:3
链表内容:[Student{name=‘李思’, age=19, gender=false}, Student{name=‘张三’, age=18, gender=false}, Student{name=‘宝玉’, age=28, gender=true}]
是否存在s1:true
是否存在s2:true
s1的hashcode:1163157884
s1的hashcode:1956725890

对于使用散列表的情况:

public class SetRemo {

    public static void main(String[] args) {
        Set<Student> hs=new HashSet<>();
        Student a1 = new Student("a","12");
        hs.add(a1);
        Student a2 = new Student("a","12");
        hs.add(a2);
        hs.add(new Student("b", "12"));
        hs.add(new Student("c", "13"));


        System.out.println("a1哈希值:"+a1.hashCode());
        System.out.println("a2哈希值:"+a2.hashCode());
        System.out.println("a1和a2equal布尔值:"+a1.equals(a2));
        System.out.println("是否存在name:a age:12:"+hs.contains(new Student("a","12")));

        for (Object obj : hs) {
            if(obj instanceof Student) {
                Student stu=(Student)obj;
                System.out.println(stu.getName()+stu.getAge());
            }
        }

    }
}
class Student{
    private String name;
    private String age;
    private boolean gender;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public Student(String name, String age) {

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

    }




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



//    @Override
//    public int hashCode() {
//        // TODO Auto-generated method stub
//        return this.getName().hashCode()+this.getAge().hashCode();
//    }



}

只重写equal():

// 运行结果
a1哈希值:1163157884
a2哈希值:1956725890
a1和a2equal布尔值:true
是否存在name:a age:12:false
a12
c13
a12
b12

只重写hashCode():

// 运行结果
a1哈希值:1666
a2哈希值:1666
a1和a2equal布尔值:false
是否存在name:a age:12:false
a12
a12
b12
c13

同时重写hashCode()和equal():

// 运行结果:
a1哈希值:1666
a2哈希值:1666
a1和a2equal布尔值:true
是否存在name:a age:12:true
a12
b12
c13

上面用到了散列表HashSet(), 但只重写了equal()方法,equal()给的结果是true,如果按照之前的思路,集合里不会出现重复的a12,但这里出现了。
原因:两个a12的hashCode()值不同,就认为是不同的对象,加入到了集合中去了,所以还要重写hashCode()方法。
可以得知Set必须同时同时重写hashCode()和equal().

总结:

第一个例子重点在于判断是否相同,那么有equal()被重写就够了,hashcode()可以不重写 。

第二个例子在于判断是否不同,由于先通过hashcode()判断是否不同,没有重写的话,一定就是不同,就会加入进去,即使重写了hashcode(),结果2个对象的hashcode相同,大概率说明了2个对象属性相同,但有可能在小概率上2个对象属性不同,却碰巧计算到了相同的hashcode,那么还要判断equal(),没有重写的话,一定就是不同,仍然会加入进去,所以还要重写equal(),说明2个对象相同属性,这样重复属性的对象就不会加入进去了。这种情况下hashcode()和equal()都必须重写。

综合上面2种情况,为了规范,hashcode()和equal()都应该重写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值