说⼀说hashCode()和equals()的关系
1.是什么
hashCode()
和 equals()
是 Java 中两个非常重要的基础方法,主要用于对象的比较和哈希相关操作。它们的关系在于:如果两个对象根据 equals()
方法相等,那么它们的 hashCode()
必须相同,但反过来并不一定成立。理解它们的关系非常关键,特别是在使用哈希表数据结构(如 HashMap
、HashSet
)时。
1. equals()
方法
equals()
方法用于比较两个对象是否相等。默认情况下,它比较的是对象的引用(即内存地址),但通常我们会根据对象的实际内容来重写这个方法。例如,在 Person
类中,我们可能会根据 name
和 age
属性来判断两个 Person
对象是否相等。
2. hashCode()
方法
hashCode()
方法返回对象的哈希码值,这是一个整数。哈希码用于确定对象在哈希表中的索引位置。理想情况下,不同的对象应该产生不同的哈希码,但这并不是强制的。重要的是,如果两个对象通过 equals()
方法比较是相等的,那么它们的 hashCode()
方法必须返回相同的整数值。
3.hashCode()
和equals()
的关系
-
等价性(Equality):
- 如果两个对象根据
equals()
方法是相等的,那么这两个对象必须具有相同的哈希码。即o1.equals(o2)
为true
时,o1.hashCode()
必须与o2.hashCode()
返回相同的整数值。 - 这是因为基于哈希的集合(如
HashMap
)使用hashCode()
来快速定位对象所在的桶(bucket),然后使用equals()
来检查对象是否真的相等。
- 如果两个对象根据
-
不一致性(Inconsistency):
- 如果两个对象的
hashCode()
返回相同的值,这并不意味着这两个对象必须相等。不同的对象可能有相同的哈希码,这就是所谓的哈希冲突。 - 但是,如果两个对象的
hashCode()
返回不同的值,那么这两个对象一定不相等。
- 如果两个对象的
总结:
- 如果两个对象相等(即
equals()
返回true
),则它们的hashCode()
值必须相等。否则,基于哈希的集合(如HashMap
、HashSet
)在存储和查找对象时会发生错误。 - 如果两个对象的
hashCode()
值相等,它们不一定相等(即equals()
不一定返回true
)。但这样会导致哈希表中不同对象落入同一个“桶”,从而可能影响查找性能。
4.为什么要重写 hashCode()
和 equals()
equals() ⽅法⽤于⽐较两个对象的内容是否相等。在Java中,默认实现是⽐较对象的引⽤,即⽐较两个对象是否指向内存中的相同位置。但通常,我们希望⽐较对象的内容是否相等。鉴于这种情况,Object类中 equals() ⽅法的默认实现是没有实⽤价值的,所以通常都要重写。⽽由于hashCode()与equals()具有联动关系(如果两个对象相等,则它们必须有相同的哈希码),所以equals()⽅法重写时,通常也要将hashCode()进⾏重写,使得这两个⽅法始终保持⼀致性。
代码示意:
不重写 hashCode()
和 equals()
方法
默认情况下,equals()
和 hashCode()
是基于对象的内存地址比较的:
class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}
}public class Main {public static void main(String[] args) {Person p1 = new Person("John", 25);Person p2 = new Person("John", 25);System.out.println(p1.equals(p2)); // 输出:false,默认比较的是内存地址System.out.println(p1.hashCode() == p2.hashCode()); // 输出:false,hashCode 基于内存地址}
}
重写 equals()
但不重写 hashCode()
如果你只重写了 equals()
而没有重写 hashCode()
,则会出现问题:即使两个对象根据 equals()
相等,它们的 hashCode()
可能不同。这会导致哈希表无法正确处理它们。
class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic 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 Main {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 不相等// 放入 HashSet 中HashSet<Person> set = new HashSet<>();set.add(p1);set.add(p2);System.out.println(set.size()); // 输出:2,应该是1(因为 equals 相等),但 hashCode 不同导致重复}
}
重写 equals()
和 hashCode()
如果我们重写了 equals()
方法,还必须重写 hashCode()
方法来保证相等的对象有相同的哈希值。这样,哈希表会正确处理这些对象。
class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic 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);}@Overridepublic int hashCode() {return name.hashCode() + age; // 基于对象的属性计算 hashCode}
}public class Main {public static void main(String[] args) {Person p1 = new Person("John", 25);Person p2 = new Person("John", 25);System.out.println(p1.equals(p2)); // 输出:trueSystem.out.println(p1.hashCode() == p2.hashCode()); // 输出:true// 放入 HashSet 中HashSet<Person> set = new HashSet<>();set.add(p1);set.add(p2);System.out.println(set.size()); // 输出:1,说明 p1 和 p2 被认为是相同的对象}
}
5. 总结
- equals() 用于比较两个对象是否相等,通常基于对象的内容来比较。
- hashCode() 用于生成对象的哈希值,主要用于提高基于哈希表数据结构的效率。
- 如果重写了
equals()
,务必也要重写hashCode()
,以确保相等的对象拥有相同的哈希值,从而保证哈希表中的正常操作。 - 如果两个对象
equals()
返回true
,它们的hashCode()
必须相同;但hashCode()
相同的对象不一定equals()
返回true
。