恪守equals和hashCode方法的通用约定
遵守equals和hashCode方法的通用约定
在覆盖了equals方法的类中,也必须覆盖hashCode方法。否则违反了Object.hashCode的通用约定会导致该类无法和基于散列的集合(HashMap,HashSet和HashTable)一起正常使用。
如下约定内容摘自Object规范:
1. 在应用程序中,只要对象的euqals方法的比较操作所用的信息没有修改,那么对于同一个对象的调用多次hashCode,必须始终如一返回同一个哈希值。
2. 如果两个对象通过equals比较相等,那么它们的哈希值相同。
3. 如果两个对象通过euqals比较不等,他们的哈希值可能相同,取决于hashCode的实现,由此散列表的性能也会有区别。
以前面的PhoneNumber类为例,编写了如下的测试用例:
发现测试失败了,但是两个对象通过equals比较是相等的。由于并没有覆盖hashCode方法导致两个相等的对象不能获得相同的散列码。根据约定重写hashCode:
好的散列函数通常倾向于"为不相等的对象产生不相等的散列码", 否则会引起冲突,使散列表想链表退化。计算是可以把冗余的域排除在外。注意不要试图从散列码计算中排除关键域来提高性能。
在覆盖了equals方法的类中,也必须覆盖hashCode方法。否则违反了Object.hashCode的通用约定会导致该类无法和基于散列的集合(HashMap,HashSet和HashTable)一起正常使用。
如下约定内容摘自Object规范:
1. 在应用程序中,只要对象的euqals方法的比较操作所用的信息没有修改,那么对于同一个对象的调用多次hashCode,必须始终如一返回同一个哈希值。
2. 如果两个对象通过equals比较相等,那么它们的哈希值相同。
3. 如果两个对象通过euqals比较不等,他们的哈希值可能相同,取决于hashCode的实现,由此散列表的性能也会有区别。
以前面的PhoneNumber类为例,编写了如下的测试用例:
@Test
public void testHashCode(){
PhoneNumber pn1 = new PhoneNumber(86, "12345");
PhoneNumber pn2 = new PhoneNumber(86, "12345");
Map<PhoneNumber,String> map = new HashMap<PhoneNumber,String>();
map.put(pn1, "12345");
assertNotNull(map.get(pn2));
}
发现测试失败了,但是两个对象通过equals比较是相等的。由于并没有覆盖hashCode方法导致两个相等的对象不能获得相同的散列码。根据约定重写hashCode:
@Override
public int hashCode(){
int result = 17;
result = 31 * result + countryCode;
if(nationalNumber != null)
result = 31 * result + nationalNumber.hashCode();
return result;
}
好的散列函数通常倾向于"为不相等的对象产生不相等的散列码", 否则会引起冲突,使散列表想链表退化。计算是可以把冗余的域排除在外。注意不要试图从散列码计算中排除关键域来提高性能。