hashCode()和equals()之对象对比
参考以下两文
https://mp.weixin.qq.com/s?__biz=MzIyNDU2ODA4OQ==&mid=2247483944&idx=1&sn=3a81af53aaaac56b60e50c1619433513&chksm=e80db45edf7a3d48807151107efd8d2b39f04424b6b1fe57212a92ea2b4927702a5250c37fa7&scene=21#wechat_redirect
https://www.cnblogs.com/keyi/articles/7119825.html
杂谈:
当equals方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规约定:值相同的对象必须有相同的hashCode;
hashCode默认是由内存地址计算的;
equals默认比较对象内存地址,即使两个属性值相同的对象内存地址也一定不同(两个对象的内存地址一定不同);
因为重写的equal()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,那么hashCode()既然效率这么高为什么还要equal()呢?
HashCode相等,equals不一定为True (HashCode算法公式问题(哈希碰撞?),极小概率不同对象会得到一样的hashCode),所以默认的hashCode不绝对可靠;
重写规矩:
equals() 通常进行同类对象的属性比较;
equals() True,hashCode必须相等;
equals() False, hashCode必须不相等;
如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同(内存地址不一样)但equals()方法判断出来的结果为true,这样不利于我们比较属性值一样的两个对象。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name.equals(person.name);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + age;
return result;
}
重写的equal()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,但不可靠
所以每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(不必再用equal()去对比了),如果hashCode()相同,再使用equal()对比,如果为True,则表示这两个对象是真的相同了,这样大大提高了效率也保证了对比的准确性,实际应用就是Hash容器。
举例,创建Person类的HashSet集合,必须同时覆盖Person类的equals() 和 hashCode()方法。
HashSet不保存重复元素,靠的就是先判断hashCode是否相等,如果相等再进行equals()比较
HashMap底层也是通过HashSet实现,HashMap会覆盖键相同的值
原则总结(摘抄):
1.同一个对象(没有发生过修改)无论何时调用hashCode()得到的返回值必须一样。
如果一个key对象在put的时候调用hashCode()决定了存放的位置,而在get的时候调用hashCode()得到了不一样的返回值,这个值映射到了一个和原来不一样的地方,那么肯定就找不到原来那个键值对了。
2.hashCode()的返回值相等的对象不一定相等,通过hashCode()和equals()必须能唯一确定一个对象。
不相等的对象的hashCode()的结果可以相等。hashCode()在注意关注碰撞问题的时候,也要关注生成速度问题,完美hash不现实。
3.一旦重写了equals()函数(重写equals的时候还要注意要满足自反性、对称性、传递性、一致性),就必须重写hashCode()函数。
而且hashCode()的生成哈希值的依据应该是equals()中用来比较是否相等的字段。