hashcode与equals的区别

Lou.Chen
大约 5 分钟

hashcode与equals的区别

一、hashCode()和equals()是什么?

hashCode()方法和equals()方法的作用其实一样,在Java里都是用来对比两个对象是否相等一致。

二、Object默认的hashcode()和equals()

equals:

默认比较的为对象的地址

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

hashcode:

使用对象的内存地址编号

public native int hashCode();

三、Integer和String的equals()和hashcode()

1、String

equals:

对Object进行实现,比较的是每个字符

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

hashcode:

对Object进行实现,计算的是每个字符对应的ASCII码+ 31乘以上一个的ASCII码

public int hashCode() {
    	//hash默认为0
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

2、Integer

equals:

直接比较的该数字

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
}
public int intValue() {
        return value;
}

hashcode:

也是返回的该数值

public int hashCode() {
        return Integer.hashCode(value);
}
public static int hashCode(int value) {
        return value;
    }

3、测试

注意:满足下列条件的,必须要实现hashcode和equals方法

①equals比较值相等的,hashcode一定相等

我们可以直接使用Integer和String的equals来比较,因为已经默认帮我们实现了其父类的hashcode和equals方法

        String a="abb";
        String b="abb";
		//true
        System.out.println(a.equals(b));
		//96353
        System.out.println(a.hashCode());
		//96353
        System.out.println(b.hashCode());

        Integer c=162;
        Integer d=162;
 		//162
        System.out.println(c.hashCode());
		//162
        System.out.println(d.hashCode());
		//true
        System.out.println(c.equals(d));

注意:所有的包装类对象之间值的比较,全部使用equals方法比较。

说明:对于Integer var=?在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。

Integer a = 235;
Integer b = 235;
if (a.equals(b)) {
    // code
}
②hashcode相等的,其值不一定相等

从重写hashcode的规则可以得出

        String a1="a";
        Integer b1=97;
		//true  
        System.out.println(a1.hashCode()==b1.hashCode());

三、从对象中进一步得出hashcode和equals的区别

1、未重写hashcode与equals:

public class Student {
    private String name;
    private Integer age;
	public Student() {
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    //getter setter tostring
}
        Student s1 = new Student("张三", 11);
        Student s2 = new Student("张三", 11);
//        false
        System.out.println(s1.equals(s2));
//		  1956725890
        System.out.println(s1.hashCode());
//		  356573597
        System.out.println(s2.hashCode());

我们发现默认使用的equals和hashcode的方法为Object中的方法,equals比较的是对象的地址。hashcode计算的是默认的地址编号。所以我们必须要重新hashcode和equals方法。

2、重新hashcode与equals:

public class Student {
    private String name;
    private Integer age;
	public Student() {
    }

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

        Student student = (Student) o;

        if (!Objects.equals(name, student.name)) {
            return false;
        }
        return Objects.equals(age, student.age);
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (age != null ? age.hashCode() : 0);
        return result;
    }
    
    //getter setter tostring
}

        Student s1 = new Student("张三", 11);
        Student s2 = new Student("张三", 11);
//      true
        System.out.println(s1.equals(s2));
//      24021570
        System.out.println(s1.hashCode());
//      24021570  
        System.out.println(s2.hashCode());

这次我们发现,使用equals判断相等,hashcode也一致

equals判断的是不是同一对象,若为同一对象,那么我们直接比较其对象的值,若都相等返回true,否则返回false

hashcode计算的是每一个属性值计算成的hashcode值进行的累加操作。

3、结论

从Integer和String类和Student类综合得出:

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

四、hashCode()和equals()在集合中的使用

1、对于需要大量并且快速的对比的话如果都用equals()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equals()去再对比了),如果hashCode()相同,此时再对比他们的equals(),如果equals()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!

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

例如,我们通过HashSet来存储对象,其默认的形式是无序,且唯一。所以HashSet来做去重判断的时候,首先会去比较hashcode,若hashcode不等,则不添加此值。否则hashcode相等再去比较equals,若相等,则添加此值,否则,不添加此值

        HashSet<Student> hashSet=new HashSet<>();
        hashSet.add(new Student("zs", 11));
        hashSet.add(new Student("zs", 11));
        Iterator<Student> iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

未重写hashcode与equals的Student类:

Student{name='zs', age=11}
Student{name='zs', age=11}

我们发现没有去重。

重写hashcode与equals的Student类:

Student{name='zs', age=11}

我们发现已经去重