最近实在是太忙了,许久没有写写技术博客了. 最近发现一个挺有意思的hashcode 与equals 的现象
我们先预设一个条件,假设有一个person.class,我们的hash方法是 如果两个对象的name 和age都相同 那么我们就认为这两个对象 在同一个hashset里面是相同的元素,是需要覆盖的。
但是我发现发现一个挺有趣的现象,如果单单重写对象hashCode方法,hashmap进行插入的时候 并不会生效.
public class EqualsAndHashCodeTest {
public static void main(String[] args) {
HashMap<Person,Integer> map = new HashMap<>();
Person p1 = new Person("a",1);
Person p2 = new Person("a",1);
map.put(p1,1);
map.put(p2,2);
System.out.println(JSONArray.toJSON(map));
}
static class Person {
private String name;
private int age;
// @Override
// public boolean equals(Object o) {
// if (this == o) return true;
// if (!(o instanceof Person)) return false;
// Person person = (Person) o;
// return age == person.age &&
// Objects.equals(name, person.name);
// }
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
很明显 单单重写hashcode方法 并不会让我们想要进行覆盖的key 确认,那么 如果单单重写equals呢
只重写equals
上图可以看出 如果只重写equals 也并不会让我们想要进行覆盖的对象进行冲突。
同时重写hashcode 和equals
从上图我们可以看到 唯有同时重写hashcode 和equals 且满足我们预设的条件 我们的hashmap才会认为是相同的key。
为什么呢?
为什么会这样 单单重写hashcode 或者equals 并不会让元素的预设规则生效呢?
我们进入hashmap的内部实现
从上图可以看出 put具体就调用了
putVal(hash(key), key, value, false, true)
这个方法hashmap计算对象hash值的时候 会调用对象的hashcode方法。此时 重写hashcode 方法 ,并且person的 age 和name相同的话 得到的hash是相同的
然后我们进入putVal内部
这边可以明显的看到,如果两个对象hash值相同,hashmap还会调用对象的equals方法,如果equals方法也相同,他才会进行覆盖!
问题发生的原因:
hashmap进行put的时候 ,会先比较对象和已有对象的hash值 如果hash值相同,再调用equals方法。
因为obejct.equals 默认比较的是对象的地址值
而object.hashcode()默认生成的是对象的地址值
所以 这两个方法必须都重写 才可以合理使用hashmap 来提高程序性能.
更深一步 为什么java那么设计呢?
- 如果没有hashcode 只有equals,那么我们在hash表里面插入数据,需要一个个和现有数据比对,时间复杂度就是o(n)。查询复杂度也是o(n)
- 假如没有equals 只有hashcode,那么 就会有可能发生 hashcode相同,但是person的 age 和name 不完全相同的情况,这个在技术上是解释合理的.但是工程上 应该很难有人会想用hash结构了吧。。。
- 所以那么设计很合理。