Java面试题:hashcode相等两个类一定相等吗?equals呢?相反呢?
今天我们来聊一聊一个常见的Java面试题:hashCode相等,两个对象一定相等吗?equals呢?相反呢?
这个问题经常出现在Java面试中,很多人回答时都会有点困惑。其实它考察的不仅仅是你对hashCode()和equals()方法的理解,更是你对Java对象比较机制的深度认知。
我自己第一次遇到这个问题时,感觉自己答得还算可以,但是回去深挖了下发现,嘿,原来我并没有完全理解这其中的细节。所以今天咱们就一起来理清这个问题,把它讲透。
先聊聊 hashCode() 和 equals() 这两个方法
首先,我们得理解一下这两个方法的作用。说白了,hashCode() 和 equals() 都是用来比较对象的。但是它们在底层的工作机制和比较方式上有着本质的不同。
hashCode():
hashCode()是一个方法,定义在Object类中。它的作用是返回对象的哈希值,这是一个整数,通常用于存放对象在散列结构中(比如HashMap,HashSet)的位置。比如说, hashCode()就像是一个对象的“身份证号码”,在哈希表中会用来快速定位对象所在的桶(bucket)。但它并不保证两个对象的哈希值必须唯一,只是说两个对象的哈希值相等时,它们有可能是相同的对象,但不能证明它们必定是相同的。
equals():
equals()也是定义在Object类中的方法,通常用来比较两个对象是否相等。与hashCode()不同,equals()是用来比较对象的内容,而不是它们在内存中的位置。如果我们在自定义类中覆盖了 equals(),那就意味着我们为该类定义了什么样的“相等标准”。比如,对于一个Person类来说,equals()可能会比较name和age是否一致,而不仅仅是判断它们是否是同一个对象的引用。
回到问题:“hashCode 相等,两个对象一定相等吗?equals 呢?相反呢?”
1. hashCode() 相等,两个对象一定相等吗?
这里的答案是不一定。我们来做个简单的示例:
class Person {
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return name.hashCode() + age; // 假设简单计算hashCode
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("John", 25);
Person p2 = new Person("John", 25);
System.out.println(p1.hashCode() == p2.hashCode()); // true
System.out.println(p1.equals(p2)); // true
}
}
在这个例子中,我们定义了一个 Person 类,重写了 hashCode() 和 equals()。我们可以看到,虽然 p1 和 p2 的内容相同(名字和年龄一样),它们的 hashCode() 相等,但它们依然是两个不同的对象。如果你直接比较它们的引用地址(通过 ==),结果会是 false,因为它们是不同的实例。
但是如果你调用 equals(),因为我们在 equals() 方法中检查了 name 和 age,那么它们被认为是相等的。
所以说,hashCode() 相等并不能保证两个对象是相等的,除非你也重写了 equals(),并且在 equals() 中按照某种规则判断它们是否相等。
2. equals() 相等,两个对象一定相等吗?
如果两个对象通过 equals() 比较是相等的,那它们的内容在逻辑上是一样的。不过这并不代表它们的哈希值一定相同。比如你重写了 equals(),但是没有重写 hashCode()。在这种情况下,两个内容相同的对象,equals() 会返回 true,但是它们的 hashCode() 可能不相同。这样,哈希表就不能确保它们在 HashMap 或 HashSet 中被正确定位。
举个例子:
class Person {
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("John", 25);
Person p2 = new Person("John", 25);
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode() == p2.hashCode()); // false (如果没有重写hashCode)
}
}
这里我们没有重写 hashCode(),所以 p1 和 p2 虽然在 equals() 上返回 true,但它们的 hashCode() 可能不相等,这会导致它们在哈希表中的行为不符合预期。
3. 相反的情况:hashCode 不等,equals 一定不等吗?
如果两个对象的 hashCode() 不相等,那这两个对象一定不相等。为什么呢?因为哈希值本身是计算对象内容的一部分。两个对象如果有不同的 hashCode(),那它们肯定不相等。这个规则是由 hashCode() 方法的设计所决定的。
实际上,Java 的规范就明确要求:如果两个对象的 hashCode() 不相等,它们通过 equals() 比较时一定不相等。所以如果你在实现 hashCode() 方法时没有遵循这个规则,那就可能导致一些意料之外的结果,甚至会影响到 HashMap 和 HashSet 的行为。
总结
总的来说,hashCode() 和 equals() 的关系其实很微妙。它们的相互配合决定了对象在哈希表中的行为。如果你想要保证对象在 HashMap 或 HashSet 中正确的比较和存储,记得遵循 Java 的规范:如果重写了 equals(),一定要同时重写 hashCode(),并且要确保 hashCode() 的一致性。
至于面试时的答案,记住:hashCode 相等并不意味着 equals 相等,而 equals 相等也不一定 hashCode 相等。这两个方法的实现需要符合 Java 的约定,才能保证它们在各种数据结构中的正确性。
那么,我猜你现在一定能在面试时轻松应对这个问题了吧!😎
-END-
以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言。