目录
- 一、问题分析
- 1.equals() 的实现
- 2.equals() 和 hashCode() 的关系
- 3.存在的问题
- 二、完整回答
一、问题分析
1.equals() 的实现
关于这个问题,首先需要深入了解一下 equals() 这个方法。
String 类 equals() 源码如下:
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;
}
从代码中可以看到,当调用 equals() 方法去比较两个对象的时候,会做两个操作:
- 使用
==
号去比较两个对象的的内存地址,如果地址相同则返回 true。 - 如果第1个操作没有满足,就会继续去比较这两个字符串的值。如果这两个字符串的值完全相同,同样返回 true。
那 equals() 和 hashCode() 有什么关系呢?
2.equals() 和 hashCode() 的关系
首先,Java 里面任何一个对象都有一个 native
的 hashCode() 方法。
其次,这个 hashCode() 方法在散列集合中会用到,比如:HashTable、HashMap 这样一些集合类。当往这样一些集合类添加元素的时候,需要判断元素是否存在。而如果直接使用 equals() 的话效率太低,所以一般是直接使用对象的 hashCode 的值进行取模运算。如果 table 里面没有这个对象的 hashCode 对应的值,那么它就可以把这个对象直接存进去。不用再进行任何比较。
而如果存在的话,就需要调用它的 equals() 方法,与新元素进行比较。相同的话就直接覆盖,不相同就散列到其他的地址。
所以这里面存在一个冲突解决的问题。这样一来,实际调用 equals() 方法的次数就大大降低了。
hashCode 的值,默认是 JVM 使用随机数来生成的,两个不同的对象可能生成的 hashCode 会相同。那么这种情况下,在 Hash 表里面体现就是所谓的 哈希冲突 。通常会使用链表或线性探测等方式来去解决这个冲突的问题。但是如果两个完全相同的对象,也就是内存地址指向同一个,那么他们的 hashCode 一定是相同的。
了解了 equals() 方法和 hashCode() 方法的关系以后,我们再来分析一下这个面试题。
3.存在的问题
在理论情况下,假设 x.equals(y) == true
,如果没有重写 equals() 方法,那么这两个对象的内存地址一定是同一个,意味着 hashCode 必然是相等的。
但是如果我们之重写了 equals() 方法,就有可能导致 hashCode 不相同。一旦出现这样的情况,就会导致这个类无法和所有集合类一起工作。
所以在实际开发过程中,约定俗成的一条规则:
重写 equals() 方法的同时,也需要重写 hashCode() 方法。
下面我们来看一下关于这个问题的完整的回答:
二、完整回答
-
如果只重写 equals() 方法,不重写 hashCode() 方法,就可能会导致 a.equals(b) 这样一个表达式成立,但是 hashCode 却不同。
-
那么这个只重写了 equals() 方法的对象,在使用散列集合进行存储的时候就会出现问题。因为散列集合是使用 hashCode 来计算 key 的存储位置,如果存储两个完全相同的对象,但是有不同的 hashCode,就会导致这两个对象存储在 hash 表的不同位置。当我们想要去根据这个对象去获取数据的时候,真实拿到的数据和我们实际想要的数据就可能存在偏差,从而导致系统出现不可预知的错误。
整理完毕,完结撒花~ 🌻