1. 简介
我们都知道,equals()与hashcode()是Object类中的两个非常重要的方法。equals是为了方便对比两个对象的异同、hashcode负责获取对象的散列码,方便在一些集合操作中能用0(1)的时间复杂度找到我们想要的对象。
首先,默认的equals方法为:
public boolean equals(Object obj) {
return (this == obj);
}
只有当两个对象引用同一个对象(指向同一个内存地址)时才返回true。但是很多时候我们仅仅想要对比两个对象代表的“值”是否相等,而不是判断这两个引用是否指向同一块地址。这时,我们就需要在我们的类中重写equals方法,通过对象的属性值判断两个对象是否相等。例如:
public class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object obj) {
//判断是否引用同一地址
if (obj==this)
return true;
//判断是否为null或者类型不同
if (obj==null || obj.getClass()!=this.getClass())
return false;
//转换类型
Student student = (Student) obj;
return student.age==this.age && (this.name==student.name || (this.name!=null && this.name.equals(student.name)));
}
}
这样我们就可以通过对比两个对象的某些属性值来判断两个对象是否相等,但是我们仍未重写Student类的hashcode方法,下面我们去详细分析重写hashcode的必要性和重要性。
2. 为什么重写equals方法的同时也必须重写hashcode方法?
首先我们要知道hashcode与equals的编写规则: (1)通过equals方法判断的两个对象的结果为true的话,那么调用他们的hashcode方法也必须得到相同的散列值; (2)通过equals方法判断为false的两个对象的hashcode所得到的散列值也可能相同。 那么,为什么重写equals方法时必须重写hashcode呢?
我们可以这么理解,如果我们编写的类的实例如果有可能被放入到以hashcode为基础的容器中的话,就必须重写hashcode。由于大多数情况下我们不能断言这个类的实例永远不会被放入到hashMap、hashTable等容器中,所以最好的方法是在重写equals方法时重写hashcode方法。
原因
以HashMap为例,我们知道get、put是HashMap中十分重要的方法,当我们想要将一个键值对key-value放入HashMap中时,需要得到该key的hashcode,通过hashcode指定放置数组的位置坐标,如果通过key的hashcode方法得到的散列码对应的数组中已经存在内容了,就会再通过key的equals方法判断,如果equals方法的结果为true的话,就说明两个key相同,将新value覆盖旧value;如果equals方法的结果为false,则在以该元素为头节点的链表中继续查找,直到找到相同的key或者在尾部新建一个节点。 如果没有重写hashcode方法,会出现下面两种情况: (1)如果两个key的值想用,也即equals判断为true,但是不是指向同一对象,那么通过第二个key对象的hashcode去寻找与其值相同的key时,两者因为不是指向同一地址,hashcode极大可能不同,也就意味着找到的HashMap的数组的下标不同,在这个数组的下标中,当然无法通过equals方法找到相等的key;这样就会造成两个equals判断相等的key,无法通过一个key去get到这个key在HashMap中所对应的值,get的结果将会为null; (2)如果通过equals方法判断为false的两个key对象,她们两个的hashcode如果相同的话,将会在同一个数组下标中保存两个key值,但是这样用equals就找不着这个key了。