为什么重写equals方法时一定要重写hashCode方法?

重写hashCode方法时必须要重写hashCode方法实际上是为了迁就java的源码设计。因为在通过hashCode值进行数据管理的java集合(主要是Set式和Map式的集合)中,java源码为了实现判断相等时的查找效率,它是首先判断两个对象的hashCode值是否相等,在两者hashCode值相等的情况下,才调用equals方法。

也就是说如果我们重写了equals方法,使两个对象的属性相同就认定两个对象相等,却没有重写hashCode方法的话,就算两个对象属性相同,最后也会被认定为时两个相同的对象。因为java是先帮我们判断的hashCode是否相等,而这两个属性相同的对象hashCode值肯定不同。因为对象的hashCode值和它自身的存储地址相关,而不同的对象肯定有不同的存储地址。

所以我们在重写equals方法时一定要重写hashCode方法,并且一定要保证在重写的equals方法判断两个对象相等的时候,两者的hashCode值一定相等。另外,还要注意一点的是,对于某些属性值可能发生频繁变化的对象,我们在重写其所属类中的hashCode方法时,hashCode值的获取一定不能过于依赖这些属性。否则可能会发生明明是同一个对象,但取值前后的hashCode值不一致的情况,这会造成该对象无法被获取,从而引发“内存泄漏”。

当然,这里还有一点要注意的是,虽然对象的hashCode值和它自身的存储地址有关,但是并不是对象的存储地址决定了它的hashCode值,而是它的hashCode值决定了它的存储地址而对象的hashCode如何获得,它是通过哈希算法对数据信息进行有损压缩以后得到的一个固定长度的值。

在获取hashCode值的时候,在极低的概率下有的时候会出现"散列碰撞"的情况,就是说两个八竿子打不着的对象它们的hashCode值可能是相等的。前面已经说过对象的hashCode值决定了它的存储地址,那这种“散列碰撞”的两个对象会被分配在同一个hash“槽位”中(一个槽位可以放置多个元素),这会导致查找性能下降。

但是其实在最后,我们可以抬个杠:我们其实只有在将对象放入通过hashCode值进行数据管理的java集合中的时候,才有“重写equals方法时一定要重写hashCode方法”的需求;也就是说如果你确定某类对象以后不会有被放入通过hashCode值进行数据管理的java集合中的机会,你重写了equals方法不重写hashCode方法其实也没事,不过这种应用场景挺少的。

下面给出重写了equals方法却没有重写hashCode方法时,两个对象属性相同而hashCode值不同,最后虽然equals方法判定为true;但在将两个对象被放入HashSet中时却被认定为是不同对象的测试代码:

import java.util.HashSet;

public class EqualsHashCode {
	public static void main(String[] args) {
		Cat cat1 = new Cat("Tom",6);
		Cat cat2 = new Cat("Tom",6);
		System.out.println("首先进行不放入Hash式集合的相等判断:");
		System.out.println("两只猫相同吗:"+cat1.equals(cat2));
		System.out.println("猫1的hashCode值:"+cat1.hashCode());
		System.out.println("猫2的hashCode值:"+cat2.hashCode());
		
		System.out.println("##########我是快乐的分隔线############");
		
		HashSet hashSet = new HashSet();
		hashSet.add(cat1);
		hashSet.add(cat2);
		System.out.println("看到底有几只猫在说话?");
		int i=0;
		for (Cat cat : hashSet) {
			i += 1;
			System.out.println("第"+i+"只猫在说话:"+cat.toString());
		}
	}
}

class Cat{
//	这里只是做个测试,name和age就不进行私有化了
	String name;
	int age;
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Cat other = (Cat) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return name+"已经"+age+"岁了!";
	}
	
	public Cat(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public Cat() {
		super();
	}
}

运行结果是:

首先进行不放入Hash式集合的相等判断:
两只猫相同吗:true
猫1的hashCode值:2018699554
猫2的hashCode值:1311053135
##########我是快乐的分隔线############
看到底有几只猫在说话?
第1只猫在说话:Tom已经6岁了!
第2只猫在说话:Tom已经6岁了!

 

你可能感兴趣的:(Java)