为什么要重写hashcode和equals方法
1. equals和hashCode方法在哪
-
equals()和hashCode()定义在Object类中.
public boolean equals(Object obj) { return (this == obj); } public native int hashCode();
可见默认的equals方法使用的是"=="进行比较的, 因此我们经常需要重写equals方法. JDK中的很多类已经帮我们重写好了equals方法, 比如String类. (关于 “重写和重载的区别”, 以及 “equals和==的区别” 详见我的其他博文)
2. 问题的引入
- 先来看一个 实例1
@Test
public void test01(){
String phone1 = new String("中国移动");
String phone2 = new String("中国移动");
Map<String, Object> map = new HashMap<>();
map.put(phone1, 10086);
System.out.println(map.get(phone1)); // 10086
System.out.println(map.get(phone2)); // 10086
/*
* 上述 phone1 和 phone2 两个对象是相等的.
* 因此我们使用 phone1 作为 HashMap 的键, 可以使用 phone2 取出对应的值.
* phone1 和 phone2 就像是 同一把锁的两把钥匙
*/
}
- 再来看一个 实例2
// 定义一个"丈夫"类, 只有一个name属性和一个有参构造器
public class Hubby {
private String name;
public Hubby(String name) {
this.name = name;
}
}
@Test
public void test02(){
Hubby hubby1 = new Hubby("郭靖");
Hubby hubby2 = new Hubby("郭靖");
Map<Hubby, Object> map = new HashMap<>();
map.put(hubby1,"黄蓉");
System.out.println(map.get(hubby1)); // 黄蓉
System.out.println(map.get(hubby2)); // null
/*
* hubby1 和 hubby2 好像是相等的.
* 我们使用 hubby1 作为键, 使用 hubby2 却取不出来对应的值.
* 这是为什么呢?
*/
}
3. HashMap的put和get的基本原理
- 如果想解决上述案例中出现的问题, 必须先简单了解HashMap存取数据的基本原理.
- HashMap的put方法的基本原理
map.put(phone1, 10086);
- 我们要向HashMap中存入一个键值对, 首先会调用键的hashCode方法计算出一个哈希值(hash code), 然后通过这个哈希值计算出这个键值对应该放到HashMap的哪个桶中. 如果这个桶中已经有其他的元素(键值对), 那么就将我们要放的元素和桶中已经存在的元素组成一个链表放到桶中.
- HashMap的get方法的基本原理
map.get(phone1)
- 我们通过键从HashMap中取值. 首先计算键的哈希值, 然后通过这个哈希值定位到一个桶的位置, 如果桶中只有一个元素, 那么就直接将这个元素取出来. 如果桶中有多个元素(链表或红黑树), 那么就使用键的equals方法和桶中每个元素的键进行比较, 取出比较结果为true的元素(键值对).
- 可见, HashMap的put和get方法都使用到了hashCode方法,都需要计算键的哈希值; HashMap的get方法使用到了equals方法; HashMap通过hashCode方法和equals方法来判断键是不是相同.
4. 问题的解决
-
根据HashMap的原理, 我们知道, 造成上述 “实例2” 中情况的原因有两个.
①没有重写hashCode方法; ②没有重写equals方法; -
hashCode方法和equals方法的约定如下
-
如果两个对象相等, 那么他们一定有相同的哈希值(hash code)
如果两个对象的哈希值相等, 那么这两个对象有可能相等也有可能不相等(需要再通过equals来判断)
-
-
如果我们将一个对象作为HashMap的键, 那么这个对象就必须重写hashCode方法和equals方法.
当然不仅仅是作为HashMap的键, 如果将对象存入其他的集合, 其他集合的存取原理中也使用到了hashCode方法和equals方法, 那么我们也要重写这两个方法.
5. 总结
- 在某些操作中(例如HashMap的get操作)需要调用对象的hashCode方法和equals方法来判断对象是否相等, 因此我们需要重写对象的hashCode方法和equals方法.
- 注意: String类默认已经重写了equals方法和hashCode方法
- 注意: HashMap的键和值不能是基本数据类型.