Hashcode和Equals

发布时间 2023-09-08 09:54:18作者: 剑阁丶神灯

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

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

hashCode

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

public native int hashCode();

通过将该对象的内部地址转换成一个整数来实现的,这个返回值就作为该对象的哈希码值返回。

2  在不重写equals和hashCode的情况下:

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

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

3 重写equals和hashCode

3.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);
        System.out.println(p1.equals(p2)); // false
        System.out.println(p1.equals(p3)); // false
    }
}
// 不重写的情况下默认根据内存地址生成的哈希值来进行比较,内存地址不同就生成了不同的哈希值(不考虑哈希冲突),所以就插入了重复的数据。

3.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);
        System.out.println(p1.equals(p2)); // false
        System.out.println(p1.equals(p3)); // true
    }
}
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;
    }
}

3.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);
        System.out.println(p1.equals(p2)); // false
        System.out.println(p1.equals(p3)); // false
    }
 
}
class Person {
    String name;
    public int hashCode() {
        return (name==null) ? 0:name.hashCode();
    }
}

3.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);
        System.out.println(p1.equals(p2)); // false
        System.out.println(p1.equals(p3)); // true
    }
 
}
class Person {
    String name;
    public int hashCode() {
        return (name==null) ? 0:name.hashCode();
    }

    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;
    }
}

4. 总结

如果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等容器,为了避免加入重复元素,就一定要同时重写两个方法。