hashCode()与equals()方法之间千丝万缕的关系

前言

hashCode()与equals() 是java面试中经常被问道的问题,可能会被问道 “你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?” ,因此我们需要彻底弄清楚这2个方法的关联关系。

hashCode()方法介绍

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

equals()方法介绍

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

hashCode() 和 equals() 之间的关系

从类的用途这个角度来分析hashCode() 和 equals() 之间的关系

1、不会创建“类对应的散列表”

这里所说的“不会创建类对应的散列表”的意思是:我们不会在HashSet, HashTable, HashMap等等这些本质是散列表的数据结构中,用到该类。例如,不会创建该类的HashSet集合。

在这种情况下,该类的“hashCode() 和 equals() ”没有半毛钱关系!

equals() 用来比较该类的两个对象是否相等,而hashCode() 则根本没有任何作用,所以,不用理会hashCode()。

2、会创建“类对应的散列表”

这里所说的“会创建类对应的散列表”是说:我们会在HashSet, HashTable, HashMap等等这些本质是散列表的数据结构中,用到该类。例如,创建该类的HashSet集合。

在这种情况下,该类的“hashCode() 和 equals() ”是有关系的:

2.1、如果两个对象相等,那么它们的hashCode()值一定相同。这里的相等是指,通过equals()比较两个对象时返回true。

2.2、如果两个对象hashCode()相等,它们并不一定相等。因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等,此时就出现所谓的哈希冲突场景。

举例子说明:

Person类,只重写equals()方法

public class Person {

    private String name;

    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

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

测试类

public class DemoConflictTest {

    public static void main(String[] args) {

        Person p1 = new Person("eee", 10);
        Person p2 = new Person("eee", 10);
        Person p3 = new Person("aaa", 20);

        HashSet<Person> hashSet = new HashSet<>();
        hashSet.add(p1);
        hashSet.add(p2);
        hashSet.add(p3);

        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        System.out.printf("set:%s\n", hashSet);
    }

}

结果

p1.equals(p2) : true; p1(21685669) p2(2133927002)
set:[Person{name='aaa', age=20}, Person{name='eee', age=10}, Person{name='eee', age=10}]

结果分析:

Person类重写了equals方法,但是HashSet中仍然有重复元素p1和p2。是因为虽然p1和p2的内容相等,但是他们的hashCode不等,所以hashSet在添加p1和p2的时候,认为他们不相等。

为什么HashSet会用到hashCode()呢?

hashSet 底层使用的是hashMap的put方法,而hashMap的put方法,使用hashCode()用key作为参数计算出hash值,然后进行比较,如果相同,再通过equals()比较key值是否相同,如果相同,返回同一个对象。

所以,如果类使用在散列表的集合对象中,要判断两个对象是否相同,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效。

经典面试题

hashCode相等,两个对象一定相等嘛?equals相等,hashCode一定相等嘛?

结论:
hashCode相等,equals也不一定相等, 两个类也不一定相等;
equals相同, 说明是同一个对象, 那么hashCode一定相同。

哈希表是结合了直接寻址和链式寻址两种方式,所需要的就是将需要加入哈希表的数据首先计算哈希值,其实就是预先分个组,然后再将数据挂到分组后的链表后面,随着添加的数据越来越多,分组链上会挂接更多的数据,同一个分组链上的数据必定具有相同的哈希值,java中的hash函数返回的是int类型的,也就是说,最多允许存在2^32个分组,也是有限的,所以出现相同的哈希码就不稀奇了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值