对于hashCode()与equals的相关问题之前我一直是模模糊糊的,问题答案我觉得都有点答非所问。
针对hashCod与equals问题进行整理归纳:
1、equals()的作用是什么?
equals() 的作用是 用来判断两个对象是否相等。
equals() 是Object类中的方法。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:
public boolean equals(Object obj) { return (this == obj); }
意味着所有的Java类都实现了equals()方法,所有的类都可以通过equals()去比较两个对象是否相等。但它默认使用的“equals()”方法,等价于“==”方法。因此,我们通常会重写equals()方法:当两个对象的内容相等,则equals()方法返回true;否则,返回fasle。
下面根据“类是否重写了equals()方法”,分为2类:
- 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。
- 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。
以自定义User类为例:
public class User {
private String username;
private String password;
//...省略构造方法,getter,setter
public User(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
情形1:在User类中未重写equals(),创建2个内容完全一样的User对象进行比较
User类中没有对父类的equals()进行重写,那么在调用equlas()时默认会调用Object类中的equals(),在上面我们已经看了Object类中equals()的源码,实际上就是“==”比较对象的内存地址,故两个内容完全一致的User对象进行equals比较时返回的false。
情形2:User类中重写equals(),再次对两个内容一致的User对象进行equals()比较
测试类与情形1一致,需在User类中添加重写的equals()
在User类中对equals()进行重写,用于判断两个对象的内容是否一样,相同则返回true;重写后User对象在调用equals()时调用的是自己覆盖过的equals(),不再是Object类的方法
2、hashCode()的作用是什么?
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是根据当前对象在内存中的内存地址计算出来的一个int类型的整数。(-2^31~2^31-1)这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode() 的定义也在Object类中,同样意味着Java中的任何类都有hashCode() 。
虽然每个Java类都包含hashCode() ,但仅仅当创建并使用某个“类的散列表(哈希表)”时,该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),类的hashCode() 没有作用。
散列表,指的是:Java集合中本质是散列表的类(低层是HashMap),如HashMap,Hashtable,HashSet等。
通过往HashSet中添加User对象来举例说明:
若HashSet中以有10个User对象,当添加第11个User对象时会发生什么?不会挨个比较
(1)根据当前User对象的hashCode()计算出在散列表中的位置并插入
(2)对于"相同"(内容)的User对象只添加一次
3、hashCode()和equals()的联系?
老听见一句话“重写equals()时一定要重写hashCode()”,但这句话是有条件的,并非所有重写equals()的情况下都需要对hashCode()进行重写。
在上面对于hashCode()作用的理解中,也说了它是在对于有对应类的散列表时是起“作用”的(通过hashCode()查找当前对象存储在散列表中的位置)。我们往HashSet中添加User对象,以重写hashCode()和不重写hashCode()进行对照的形式来理解。
User类的代码不在赘述,同上
情形1:只重写了equals()未重写hashCode()
只重写equals(),在往Set集合中添加元素时,它首先会通过当前对象的hashCode()计算出在哈希表中的位置,若当前位置还没有元素,它会认为当前集合中的元素没重复,可以添加进来。但对于我们的需求来讲,对于u1,u2对象往Set集合中添加时只能添加一个。对于两个User对象,未重写hashCode(),那么它们的hashCode()默认调用父类的,则是通过对象的内存地址计算出的一个int整数值,相同的User对象hashCode一定相同,不同的对象hashCode()“大概率”不相同(hashCode为int整数,有取值范围,存在重复可能性)。
故在往散列表集合中添加对象时,若对于相同内容的对象不想重复添加时,我们在重写equals()的同时也需要重写hashCode()。
情形2:重写equals()同时也重新了hashCode()
我们通过User对象的两个属性的hashCode做位运算重写了hashCode()后,对于两个内容相同的不同对象所计算出的hashCode是相同的,那么在往Set集合中添加User对象时,两个对象在散列表中的存储位置是一样的,添加u2时当前位置已有元素,就不会添加成功了,达到去重的效果。
小结:
- 往散列表集合中添加对象,需同时重写equals()和hashCode()
- 两个相同的对象,它们的hashCode()一定相等
- 两个对象的hashCode()相等,这两个对象不一定相等