为什么重写对象equals方法要重写hashcode方法的真正原因!

2021-12-24 09:25:01 浏览数 (1)

javaGuide里说到了为什么要重写hashcode的原因:

3)为什么重写 equals 时必须重写 hashCode 方法? 如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。 hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

但是我没太理解,两个对象有相同的code他们不一定是相等的又咋样,为什么就要重写hashcode?

后面自己看了别的博文,理解了下,我觉得一定要重写hashcode的主要原因是要保障equals方法的特性,即equals返回结果必须与其hashcode比较结果必须保持一致.

为什么要这样保障呢?
1.了解hashcode是干啥的

hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

2.了解一个过程
  • 1.确定和保障对象的唯一性,我们在使用set和map的时候有下面这样一个先hashcode确定其唯一性的过程

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head First Java》第二版)。,为什么呢 ?这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

从上面两点我们来分析如果不重写hashcode会有什么后果.

我们这样去利用一个map看下他的打印结果

代码语言:javascript复制
public class Person
{
    private String name;

    private int age;

    private String sex;

    Person(String name,int age,String sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
/**
     * @see Person
     * @param args
     */
    public static void main(String[] args)
    {
        HashMap<Person, Integer> map = new HashMap<Person, Integer>();

        Person p = new Person("jack",22,"男");
        Person p1 = new Person("jack",22,"男");

        System.out.println("p的hashCode:" p.hashCode());
        System.out.println("p1的hashCode:" p1.hashCode());
        System.out.println(p.equals(p1));
        System.out.println(p == p1);

        map.put(p,888);
        map.put(p1,888);
        map.forEach((key,val)->{
            System.out.println(key);
            System.out.println(val);
        });
    }

p的hashCode:356573597
p1的hashCode:1735600054
false
false
com.blueskyli.练习.Person@677327b6
888
com.blueskyli.练习.Person@1540e19d
888

可以看到两个对象作为key值时候,比较的hashcode实际上是堆上的内存地址而我们如果我们想用name来做唯一性,需要先重写其equals

代码语言:javascript复制
public class Person
{
    private String name;

    private int age;

    private String sex;

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

    @Override public boolean equals(Object obj)
    {
        if(obj instanceof Person){
            Person person = (Person)obj;
            return name.equals(person.name);
        }
        return super.equals(obj);
    }
}

继续执行上面例子的打印,其结果如下:
其比较结果为:
p的hashCode:356573597
p1的hashCode:1735600054
true
false
com.blueskyli.练习.Person@677327b6
888
com.blueskyli.练习.Person@1540e19d
888

我们发现虽然我们已经重写了equlas,但是其在hashmap中仍然设置进去了两个name值相同的对象

代码语言:javascript复制
public class Person
{
    private String name;

    private int age;

    private String sex;

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

    @Override public boolean equals(Object obj)
    {
        if(obj instanceof Person){
            Person person = (Person)obj;
            return name.equals(person.name);
        }
        return super.equals(obj);
    }

    @Override public int hashCode()
    {
        return name.hashCode();
    }
}

如果我们想在map或者set中也根据name做其唯一性的话必须重写hashcode

代码语言:javascript复制
public class Person
{
    private String name;

    private int age;

    private String sex;

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

    @Override public boolean equals(Object obj)
    {
        if(obj instanceof Person){
            Person person = (Person)obj;
            return name.equals(person.name);
        }
        return super.equals(obj);
    }

    @Override public int hashCode()
    {
        return name.hashCode();
    }
}

p的hashCode:3254239
p1的hashCode:3254239
true
false
com.blueskyli.练习.Person@31a7df
888

总结:

  • 1,两个对象,用==比较比较的是地址,需采用equals方法(可根据需求重写)比较。
  • 2,重写equals()方法就重写hashCode()方法。
  • 3,一般相等的对象都规定有相同的hashCode。
  • 4,String类重写了equals和hashCode方法,比较的是值。
  • 5,重写hashcode方法为了将数据存入HashSet/HashMap/Hashtable(可以参考源码有助于理解)类时进- 行比较

0 人点赞