一.首先看下Java中的约定:
相等的对象必须具有相等的散列码(hashCode)
对于HashSet和HashMap这些基于散列值(hash)实现的类相等的对象必须具有相等的散列码(hashCode)
这样约定也是因为判断对象地址值相等的代价很高,所以一般会先判断两个对象的hashcode是否相同,若不相同则认为不是同一对象就没必要进行下一步骤了,若相同才去使用equals方法进而确定是否为同一个对象
二.若不遵循此约定可能导致什么问题呢?
就拿HashMap来说吧
我们都知道HashMap底层数据结构是Node数组 数组下标实际上就对应每个kv键值对存放的位置
存取数组中的元素用到的就是数组下标
判断下标是否相同的依据 就是根据hashcode值是否相同来决定 如果hashcode值不同则认为key不同
三.举个例子具体看一下
如果重写equals不重写hashcode可能出现的情况是
map中用一个对象作为key 无法正常取出
example:重写Apple类的equals方法(只要color一样即认为equals)但是不重写hashcode方法
public class Apple {
public static void main(String[] args) {
Map<Apple,String> map = new HashMap<>();
Apple apple1 = new Apple("red");
map.put(apple1,"1");
Apple apple2 = new Apple("red");
String s = map.get(apple2);
System.out.println(apple1.equals(apple2)); // true
System.out.println("s = " + s); // null
}
private String color;
// 重写equals 不重写hashcode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Apple apple = (Apple) o;
return Objects.equals(color, apple.color);
}
/* @Override
public int hashCode() {
return Objects.hash(color);
}*/
public Apple(String color) {
this.color = color;
}
public Apple() {
}
}
返回结果:
最后发现map获取的结果为null 因为很显然hashcode不相等 于是在get的时候 找不到hashcode相同的key于是直接返回null
实际上这两个对象却是equal的
四.总结几个小问题
1.为什么有了hashcode还搞一个equals方法?
因为不同的对象可能也会有相同的hashcode 这被称为hash冲突 hash算法会影响这个冲突的概率 如果只用hashcode是无法判断两个对象是否相等的
2.那为什么有了equals方法还需要hashcode呢?
这是因为equals方法性能很差 如果说在集合操作时 大量的equals就会耗费很长时间 所以通过hashcode相当于会过滤掉很多不必要的比较
3.为什么重写equals方法必须重写hashcode?
首先我们知道两个不同的对象一般来说hashcode是不同的(当然也可能出现哈希冲突 暂不考虑)
如果我们认为两个对象属性全部都相同或者部分相同就判定是这两个对象是相同的 那就需要根据这个判定依据来重写equals方法
此时如果不重写hashcode就会导致 两对象equals但hashcode却不相同
在有些场景下会首先通过hashcode判断 判断不等就认为这两个对象是不同的 就和我们的预想冲突了
例如在map或者set中 首先通过判断hashcode是否相等来判断对象是否相同 hashcode不同则认为是不同的key 就不会进行后面的判断了
这样在存取的时候都会遇到问题 存可能导致覆盖数据 取可能导致取不出来
这就是重写hashcode的意义 所以说重写equals必须重写hashcode