首先要知道为什么要重写equals方法?
那么先引出 == 和equals的区别:
- 如果两个引用类型变量使用==运算符,那么比较的是地址,它们分别指向的是否是同一地址的对象,结果一定是false,因为两个对象地址必然不同。
- ==不能实现比较对象的值是否相同。
- 所有对象都有equals方法,默认是Object类的equals,其结果与==一样。
- 如果希望比较对象的值相同,必须重写equals方法。
所以我们想要比较两个对象的内容,就必须重写equals方法
那么为什么重写equals方法时必须重写hashcode方法?
是为了提高效率,采取重写hashcode方法,先进行hashcode比较,如果不同,那么就没必要再进行equals的比较了,这样就大大减少了equals比较的次数
本来equals方法和hashcode()是不相关的,但是由于我们会在集合中放入我们创建的对象,这样由于存储结构的问题,导致在查找时二者产生了关联。
public static void main(String[] args) {
Map<MyObject, Integer> map = new HashMap<MyObject, Integer>();
HashMapTest instance = new MyObject("a");
map.put(instance, 1);
Integer value = map.get(new MyObject("a"));
if (value != null) {
System.out.println(value);
} else {
System.out.println("value is null");
}
}
//程序运行结果: value is null
我们往一个HashMap中,放入一个自定义对象 MyObject(“a”)的实例,假设我们重写了equals,然后通过get()方法获取,为何结果为空?
原因是由于hashmap的存储结构是一个hash,作用是方便快速查找,导致如果hashcode不同(hashcode默认和内存相关),2个对象new 出来,内存地址不同,那么无法找到对应的桶,就无法找到对应的对象,因此,结果就为null。所以为了保险起见,所有的自定义类,如果重写了equals方法时必须重写hashcode方法
这里引出来什么是Hash算法
Hash是散列的意思,就是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。关于散列值,有以下几个关键结论:
- 如果散列表中存在和散列原始输入K相等的记录,那么K必定在f(K)的存储位置上 ,即多次计算hash值一定相同
- 不同关键字经过散列算法变换后可能得到同一个散列地址,这种现象称为碰撞
- 如果两个Hash值不同(前提是同一Hash算法),那么这两个Hash值对应的原始输入必定不同
总结:
- hashCode主要用于提升查询效率,来确定在散列结构中对象的存储地址;
- 重写equals()必须重写hashCode(),二者参与计算的自身属性字段应该相同;
- hash类型的存储结构,添加元素重复性校验的标准就是先取hashCode值,后判断equals();
- equals()相等的两个对象,hashcode()一定相等;
- 反过来:hashcode()不等,一定能推出equals()也不等;
- 如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置