Java重写equals方法后为什么还需要重写hashcode方法?

一.equals的作用及与 == 的区别

1.equals通常用来比较两个对象的内容是否相等;

2.==用来比较两个对象的地址是否相等,equals方法默认等同于“==”;

3.Object类中的equals方法定义为判断两个对象的地址是否相等,地址相等则认为是对象相等。这也就意味着,我们新建的所有类如果没有复写equals方法,那么判断两个对象是否相等时就等同于“==”,也就是两个对象的地址是否相等。

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

二.重写equals和hashcode方法

未重写前测试:

package com.hmi.entity;

public class Person {
	private String name;
	private int age;
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public Person() {
	}
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
}



package com.hmi.test;

import com.hmi.entity.Person;

public class EqualsTest {
public static void main(String[] args) {
	Person p1 = new Person("小明",18);
	Person p2 = new Person("小明",18);
	System.out.println(p1.equals(p2));
	System.out.println(p1==p2);
    System.out.println(p1.hashCode()==p2.hashCode());
}
}

输出结果:

重写后的测试:

@Override
	public boolean equals(Object obj) {
		//如果为同一对象的不同引用,则返回true
		if (this == obj) {
			return true; 
		}
		//如果传入对象为空,或者俩对象属于不同的类型。则返回false
		if (obj == null || this.getClass() != obj.getClass()) { 
			return false;
		}
		//对象类型相同,则判断里面的内容是否相同
		Person p = (Person) obj;
		return this.getName().equals(p.getName())
				&& this.getAge() == p.getAge();
	}



@Override
    public int hashCode() {
        //生成一个 int 类型的变量 result,并且初始化一个值,比如15
        int result = 15;
        //JVM里最有效的计算方式就是进行位运算
        // 31 * i = (i << 5) - i(左边  31*2=62,右边 2*2^5-2=62)
        // 两边相等,JVM可以高效的进行计算
        result = 31 * result + (name == null ? 0 : name.hashCode() + age);
        return result;
    }

输出结果:

三.重写equals方法为什么还要重写hashcode?

equals方法必须要满足以下几个特性:

  1.自反性:x.equals(x) == true,自己和自己比较相等

  2.对称性:x.equals(y) == y.equals(x),两个对象调用equals的的结果应该一样

  3.传递性:如果x.equals(y) == true y.equals(z) == true 则 x.equals(z) == true,x和y相等,y和z相等,则x和z相等
 

而如果某个类没有重写hashcode方法的话,equals判断两个值相等,但是hashcode的值不相等。对于一些集合就能产生冲突,比如HashMap,HashSet。

HashMap底层是由数组加链表组成的,hashcode就是数组的下标,如果不重写hashcode方法,则相同的对象会生成不同的hashcode。当插入到散列表中的时候相同的对象就会被插入,这是不符合规定的。我们知道向HashMap中插入相同值的时候会出现覆盖。

HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT = new Object();。在HashSet中,因为value值没有用,也就不存在修改value值的说法,因此往HashSet中添加元素,首先根据哈希算法判断元素(也就是key)是否存在,如果不存在这插入,如果存在就不插入,这样HashSet中就不存在重复值。如果hashcode相同,那么相同的元素会被插入到HashSet中,不符合规定。

在没有重写hashcode的情况下,往HashSet里,添加两个相同的对象:

package com.hmi.test;

import java.util.HashSet;

import com.hmi.entity.Person;

public class HashSetTest {
	public static void main(String[] args) {
		Person p1 = new Person("小明",18);
		Person p2 = new Person("小明",18);
		HashSet hs = new HashSet<Person>();
		hs.add(p1);
		hs.add(p2);
		System.out.println("HashSet.size="+hs.size());
	}
}

输出结果:

显然不符合规定,重写hashcode后:

这就是为什么重写equals方法也一定要重写hashcode方法的原因。

猜你喜欢

转载自blog.csdn.net/hm_135/article/details/104553010