Equals
Equals是Object类的一个方法,指示某个其他对象是否与此对象“相等”。 Java语言规范要求equals方法具有以下特性:
1、 自反性。对于任何非空引用x,x.equals(x)应该为true
2、 对称性。对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true。
3、 传递性。对于任何引用x、y和z。如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
4、 一致性。如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)结果不会发生变化。
5、 对于任何非空引用x,x.equals(null)返回false。
Equals与==的区别
“==” 可以用来判别基础类型或是对象是否相等,但是比较方式存在一些差异。
1、 对于基础类型数据来说,只要他们的值相等,那么就是相等的。
2、 对于对象间的比较来说,比较的就不是他们间的值而是他们的引用,只用引用的对象相等他们才会相等。
类Object的equals方法,
public boolean equals(Object obj){
return (this == obj);
}
此时同“==”没有区别。如果类没有overwrite的话,也同“==”没有任何区别。但是equals可以在子类中overwrite,来重新定义对象相等的条件。就比如String类的equals方法:
public boolean equals(ObjectanObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString =(String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
对于String类的equals方法来说,引用同一对象或是只要每个位置对应的字符相等,就可以算作是相等。
HashCode
HashCode(散列码)是由对象导出的一个整数值(可以为负数)。散列码是没有规律的。Hashcode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址(不是真正的内存地址)。当然了,子类也可以overwrite自己的hashcode,可以合理的组合实例域的散列码,以便能够让各个不同的对象产生的散列码更均匀。比如String的散列码就是依据内容导出的。
public int hashCode() {
int h = hash;
if (h == 0 && count > 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
Equals与hashcode的定义必须一致。如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。但是hashCode相等的2个对象,equals不一定返回true。例如,如果定义表示员工的类的equals条件是员工ID,那么hashCode方法就需要散列员工ID,而不是员工的名字或是存储地址。
如果存在数组类型的域,那么可以使用静态的Arrays.hashCode方法计算一个散列码,这个散列码有数组元素的散列码组成。
重写hashCode的原则
1、 不必对每个不同的对象都产生一个唯一的hashcode,只要你的HashCode方法使get()能够得到put()放进去的内容就可以了。
2、 生成hashcode的算法尽量使hashcode的值分散一些, 不要很多hashcode都集中在一个范围内,这样有利于提高散列表的性能。
重写Equals方法时需要重写hashCode方法
1、 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数
2、 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
3、 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。因为散列表(如:HashMap,HashSet)中,通过hashcode值来判定存储位置(Index),而hashcode相等只能保证两个对象在一个HASH表里的同一条HASH链上,继而通过equals方法才能确定是不是同一对象,如果结果为true, 则认为是同一对象不再插入,否则认为是不同对象继续插入。这样无形中会降低散列表的性能。可见好的hashCode生成算法是可以提高产品性能的。
所以,要满足上述的hashCode特性。且在散列表中存储数据时首先需要通过hashcode来判别存储位置,而相同的对象(equals返回true)不会重复存储,这样如果2个对象equals相同,但是没有重写hashCode方法导致返回的hashcode值不同,可以想象的到,2个对象肯定会保存在散列表的不同位置上的,尽管他们相等。这肯定是不可以的,所以就需要我们重写hashcode方法来保证这种情况不要发生。
当然,如果hashcode生成规则可以产生非常分散的hashcode值,但是在基于需要大量计算的基础上时就需要慎重考虑一下,因为这样做可能会导致一定的效率问题,因为生成hashcode的时间可能远大于查找数据的时间。需要认真的权衡。
另:基本类型不能作为散列表的key使用,是由于基本类型不是继承于Object的,在使用时需要使用它们的封装类。