在Java语言中,开发人员常常需要用到对象相等性这个概念。Java语言中,==操作符和equals()函数均可判断两个对象是否相等。但是操作符==和equals()函数的内在逻辑却不同。
==操作符和equals()函数的区别在于:其一,==是操作符,而equals()是Object类的成员方法。换句话说,所有的类都会有一个equals()成员方法。它或者继承自Object类,或者由开发人员定义。因为Java语言不支持操作符重载(Operator Overloading),所以,开发人员无法重新定义一个==操作符来比较两个对象。其二,==操作符可用来比较任何对象(包括基本数据类型(Primitive Data Type)和对象);而equals()函数只能比较对象。
操作符==是一个二元操作符,它接受两个操作数,并返回比较这两个操作数的结果。==操作符有三种应用场景。
为了能够提供基于业务逻辑(Business Logic)的等值比较,Java语言在Object类对象中添加了一个成员方法equals()。因为Java支持函数覆盖(Overriding)特性,所有的类(所有的类都是Object类的子类)都可以覆盖默认的equals()成员方法,从而开发人员能够根据业务逻辑自定义对象相等的逻辑。因此,在任何需要使用对象相等测试的代码中,均应使用equals()函数进行相等测试。例如,在容器的查询中,均使用的是equals()函数进行对象相等测试。
类似的,String作为一个特殊的类,开发人员也应使用equals()函数比较两个String对象是否包含了相同的字符。
在使用equals()函数比较两个对象时,按照业务逻辑比较这两个对象的内容可能是一个较为耗时的过程。这可能是因为比较过程逻辑复杂,也可能是这两个对象较大,需要比较的内容较多。为了加快这个比较的过程,Java在Object类中增添了hashCode()成员方法。
大致上讲,hashCode()成员方法根据对象的内容计算出一个散列值。在比较两个对象是否相等之前,可以先比较两个对象的散列值。如果两个对象的散列值不相等,那么,这两个对象一定是不相等的。如果两个对象的散列值相等,则需要进一步比较两个对象的内容。因为散列值计算速度快,因此,hashCode()成员方法常常被用于equals()函数中的第一个测试步骤,以提升对象比较效率。
另一个使用hashCode()成员方法的场景是HashMap或者有着相似功能的类。在HashMap中,hashCode()函数被用来计算对象的散列值,进而确定对象所在的Bucket和其index值。
当需要实现自定义的对象比较时,开发人员需要实现自定义的equals()函数,覆盖默认的Object.equals()函数。为了提高自定义equals()函数的效率,开发人员还可以实现自定义的hashCode()函数。在下面的例子中,类Example覆盖了equals()和hashCode()成员方法。使用@Override 标注(Annotation)是一个好的习惯,Java编译器能帮助检查函数原型是否与父类的成员函数保持一致。
import java.util.Objects;
public class Example {
@Override
public boolean equals(Example obj) {
return Objects.equals(this, obj);
}
@Override
pulibc int hashCode() {
return Objects.hash(0);
}
}
在实现自定义的equals()成员方法时,应注意以下几点,这也是equals()成员方法需要遵守的契约(Contracts)。
在实现自定义hashCode()成员方法时,hashCode()成员方法需要遵守如下契约(Contracts)。
从上述的契约来看,写好一个自定义hashCode()函数并不是一件容易的事情。在这里,小水滴给出一个经典的hashCode()函数的写法。这种实现方法能应对绝大多数的场景。总的来说,StandardHashcodeExample.hashCode()函数使用了两个素数7和31来帮助计算一个较好的散列值,降低散列值冲突(Collisions)的概率。如果成员变量是对象的话,可以直接使用该对象的hashCode()成员函数。如果成员变量是基本数据类型的话,可以直接使用它们的整数值,或者将其装箱(Boxing)为相应的对象,然后使用该对象的hashCode()函数计算散列值。最后,将每个成员的散列值综合在一起。
总之,目前没有一个哈希函数是完美的。一些IDE也能为开发人员自动生成hashCode()函数。开发人员也可以考虑直接使用下面的例子。在实际运用中,还可考虑使用两个较大的素数。
public class StandardHashcodeExample {
private int id;
private String name;
@Override
pulibc int hashCode() {
int hash = 7;
hash = 31 * hash + id;
hash = 31 * hash + (name == null ? 0 : name.hashCode());
return hash;
}
}
本章介绍了Java语言中对象相等性的逻辑,以及==操作符和equals()成员函数的用法和实现细节。总的来说,基本数据类型的数据的比较需要使用==操作符,而对象之间的比较则应使用equals()成员方法。在容器的实现,或者搜索算法的实现中,都使用equals()成员方法测试元素的相等性。因此,小水滴也建议,开发人员应多使用对象和equals()成员方法,尽量减少在函数接口中使用基本数据类型。
注册用户登陆后可留言