重写equals和hashcode_java重写equals[通俗易懂]

重写equals和hashcode_java重写equals[通俗易懂]前几天在回顾Java基础知识的时候注意到了equals和hashCode这两个方法,仔细研究了一下瞬间就被为什么equals相等hashCode一定相等,为什么hashCode相等equals

前几天在回顾Java基础知识的时候注意到了equals和hashCode这两个方法,仔细研究了一下瞬间就被为什么equals相等hashCode一定相等,为什么hashCode相等equals不一定相等,重写equals到底要不要一起重写hashCode等问题给整懵了,在仔细分析并通过代码实践以后终于彻底搞明白了,如果有什么错误和不足欢迎指正。

目录

一、首先先来看一下原生的equals和hashCode方法。

1.1、equals

1.2、hashCode

1.3、总结

二、重写equals和hashCode的情况

2.1、都不重写

2.2、只重写equals

2.3、只重写hashCode

2.4、同时重写equals和hashCode

2.5、总结


一、首先先来看一下原生的equals和hashCode方法。

1.1、equals

Object中的equals方法和“==”是相同的,如下代码,比较的都是内存地址。

 public boolean equals(Object obj) {
   return (this == obj);
}

1.2、hashCode

 原生的hashCode方法返回的是一个根据内存地址换算出来的一个值。它的定义是这样的:

public native int hashCode();

可见这是一个native方法,因为native方法是并不是由Java语言来实现的,所以这个方法的定义中也没有具体的实现。根据jdk文档,该方法的实现一般是“通过将该对象的内部地址转换成一个整数来实现的”,这个返回值就作为该对象的哈希码值返回。

1.3、总结

所以,在不重写equals和hashCode的情况下:

        (1)两个对象如果equals相等的情况下,hashCode一定相等。因为equals默认是用“==”来比较,比较的是内存地址,而hashCode是根据内存地址得到哈希值,内存地址一样的话,得到的哈希值肯定是一样的。

        (2)两个对象hashCode相等的情况下,equals不一定相等。这是为什么呢,首先我们来说一下哈希表,哈希表结合了直接寻址和链式寻址两个方式,简单来说就是先计算出要插入的数据的哈希值,然后插入到相应的分组当中去,因为哈希函数返回的是一个int类型,所以最多也就只有2的32次方个分组,对象多了,总有分组不够用的时候,这个时候,不同的对象就会产生相同的哈希值,也就是哈希冲突现象,此时就可以通过链地址法把分组用链表来代替,同一个链表上的对象hashCode肯定是相等的,因为是不同的对象,所以内存地址不同,所以他们的equals肯定是不相等的。这的hashCode就相当于是人名,equals就相当于身份证号,同名的人多了去了,但都不是同一个人。

二、重写equals和hashCode的情况

2.1、都不重写

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";

        Person p2 = new Person();
        p2.name = "李四";

        Person p3 = new Person();
        p3.name = "张三";


        Set set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name );
        }

        System.out.println("p1.hashCode=" + p1.hashCode());
        System.out.println("p2.hashCode=" + p2.hashCode());
        System.out.println("p3.hashCode=" + p3.hashCode());
        System.out.println();

        System.out.println("p1 equals p2," + p1.equals(p2));
        System.out.println("p1 equals p3," + p1.equals(p3));
    }

}
class Person {
    String name;
}

output:

重写equals和hashcode_java重写equals[通俗易懂]

equals不同 hashCode不同

可以看到,在都不重写的情况下,插入了重复的数据。原因是不重写的情况下默认根据内存地址生成的哈希值来进行比较,内存地址不同就生成了不同的哈希值(不考虑哈希冲突),所以就插入了重复的数据。

2.2、只重写equals

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";

        Person p2 = new Person();
        p2.name = "李四";

        Person p3 = new Person();
        p3.name = "张三";


        Set set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name );
        }

        System.out.println("p1.hashCode=" + p1.hashCode());
        System.out.println("p2.hashCode=" + p2.hashCode());
        System.out.println("p3.hashCode=" + p3.hashCode());
        System.out.println();

        System.out.println("p1 equals p2," + p1.equals(p2));
        System.out.println("p1 equals p3," + p1.equals(p3));
    }

}
class Person {
    String name;

    //覆盖 equals
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof Person) {
            Person p = (Person)obj;
            return this.name.equals(p.name);
        }
        return false;
    }
}

output:

重写equals和hashcode_java重写equals[通俗易懂]

equals相同 hashCode不同

以上代码在重写equals以后虽然能够比较出对象的相同但是仍然插入了重复数据,是因为两个对象的hashCode不同所造成的,所以为了避免插入重复数据必须重写hashCode。

2.3、只重写hashCode

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";

        Person p2 = new Person();
        p2.name = "李四";

        Person p3 = new Person();
        p3.name = "张三";


        Set set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name );
        }

        System.out.println("p1.hashCode=" + p1.hashCode());
        System.out.println("p2.hashCode=" + p2.hashCode());
        System.out.println("p3.hashCode=" + p3.hashCode());
        System.out.println();

        System.out.println("p1 equals p2," + p1.equals(p2));
        System.out.println("p1 equals p3," + p1.equals(p3));
    }

}
class Person {
    String name;

    //覆盖 hashCode
    public int hashCode() {
        return (name==null) ? 0:name.hashCode();
    }
}

output:

重写equals和hashcode_java重写equals[通俗易懂]

equals不同 hashCode相同

可见以上代码还是插入了重复的数据,是因为在插入数据时,首先会比较hashCode,如果相同,再比较equals,只有equals也相同的时候,才会认为重复,因为equals不同,所以不认为是相同对象,再一次没有避免重复插入。

2.4、同时重写equals和hashCode

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";

        Person p2 = new Person();
        p2.name = "李四";

        Person p3 = new Person();
        p3.name = "张三";


        Set set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name );
        }

        System.out.println("p1.hashCode=" + p1.hashCode());
        System.out.println("p2.hashCode=" + p2.hashCode());
        System.out.println("p3.hashCode=" + p3.hashCode());
        System.out.println();

        System.out.println("p1 equals p2," + p1.equals(p2));
        System.out.println("p1 equals p3," + p1.equals(p3));
    }

}
class Person {
    String name;

    //覆盖 hashCode
    public int hashCode() {
        return (name==null) ? 0:name.hashCode();
    }

    //覆盖 equals
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof Person) {
            Person p = (Person)obj;
            return this.name.equals(p.name);
        }
        return false;
    }
}

output: 

重写equals和hashcode_java重写equals[通俗易懂]

equals不同 hashCode不同

大功告成。只插入了一个张三。

2.5、总结

如果equals方法和hashCode方法同时被重写,则需满足hashCode 的常规协定

(1)在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

(2)如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。

(3)如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能

所以,在重写方法的时候,有以下结论:

        两个对象equals相等,则它们的hashcode必须相等,反之则不一定。

(4)重写equals一定要重写hashCode吗

        答案是不一定的,如果你仅仅是为了比较两个对象是否相等只重写equals就可以,但是如果你使用了hashSet、hashMap等容器,为了避免加入重复元素,就一定要同时重写两个方法。

今天的文章重写equals和hashcode_java重写equals[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/77982.html

(0)
编程小号编程小号

相关推荐

发表回复

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