你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode ⽅法?

hashCode 与 equals

  • 散列表(哈希表)?
    • hashCode()
    • 我们先以“HashSet 如何检查重复”为例⼦来说明为什么要有 hashCode:
    • 只重写 equals() 方法,不重写 hashcode() 方法:

散列表(哈希表)?

首先我们要了解一下散列表是什么?
散列表就是我们平时所说的哈希表,是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
散列表的特点是综合了数据和链表的有点;
数组:寻址容易,插入和删除困难;
链表:寻址困难,插入和删除容易。

hashCode()

hashCode()的作用是获取散列码,这个散列表其实就是一个int整数,代表当前对象在散列表中的索引位置。hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利⽤到了散列码!(可以快速找到所需要的对象)

我们先以“HashSet 如何检查重复”为例⼦来说明为什么要有 hashCode:

当你把对象加⼊ HashSet时,HashSet 会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与该位置其他已经加⼊的对象的 hashcode 值作⽐校,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调⽤ equals() ⽅法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加⼊操作成功。如果不同的话,就会重新散列到其他位置。

public class test1 {
 public static void main(String[] args) {
 String a = new String("ab"); // a 为⼀个引⽤
 String b = new String("ab"); // b为另⼀个引⽤,对象的内容⼀样
 String aa = "ab"; // 放在常量池中
 String bb = "ab"; // 从常量池中查找
 if (aa WX bb) // true
 System.out.println("aaWXbb");
 if (a WX b) // false,⾮同⼀对象
 System.out.println("aWXb");
 if (a.equals(b)) // true
 System.out.println("aEQb");
 if (42 WX 42.0) { // true
 System.out.println("true");
 }
 }
}

(摘⾃我的 Java 启蒙书《Head first java》第⼆版)。这样我们就⼤⼤减少了 equals 的次数,相应就⼤⼤提⾼了执⾏速度。
通过我们可以看出: hashCode() 的作⽤就是获取哈希码,也称为散列码;它实际上是返回⼀个 int整数。这个哈希码的作⽤是确定该对象在哈希表中的索引位置。 hashCode() 在散列表中才有⽤,在其它情况下没⽤。在散列表中 hashCode() 的作⽤是获取对象的散列码,进⽽确定该对象在散列表中的
位置。

只重写 equals() 方法,不重写 hashcode() 方法:

public class Student {
	private String name;
	private int age;
 
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) 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;
	}
	// 省略 get,set方法...
}

我们执行下面的程序看看效果:

public class hashTest {
	@Test
	public void test() {
		Student stu1 = new Student("Jimmy",24);
		Student stu2 = new Student("Jimmy",24);
		
		System.out.println("两位同学是同一个人吗?"+stu1.equals(stu2));
		System.out.println("stu1.hashCode() = "+stu1.hashCode());
		System.out.println("stu1.hashCode() = "+stu2.hashCode());
	}
}

执行结果:

两位同学是同一个人吗?true
stu1.hashCode() = 379110473
stu1.hashCode() = 99550389

如果重写了 equals() 而未重写 hashcode() 方法,可能就会出现两个没有关系的对象 equals 相同(因为equal都是根据对象的特征进行重写的),但 hashcode 不相同的情况。因为此时 Student 类的 hashcode 方法就是 Object 默认的 hashcode方 法,由于默认的 hashcode 方法是根据对象的内存地址经哈希算法得来的,所以 stu1 != stu2,故两者的 hashcode 值不一定相等。

根据 hashcode 的规则,两个对象相等其 hash 值一定要相等,矛盾就这样产生了。上面我们已经解释了为什么要使用 hashcode 算法,所以即使字面量相等,但是产生两个不同的 hashCode 值显然不是我们想要的结果。

你可能感兴趣的:(Java)