Effective Java (9) 覆盖equals时总要覆盖hashCode

时间:2021-05-12 16:23:10

这个是一直强调的了。

java规范中Object规范要求同一对象的hashCode多次调用保持不变,对象相等则hashCode必须相等,反之对象不相等,hashCode不必不相等,只是不相等的hashCode会改善散列表的性能。

所以在覆盖equals时,一定要将hashCode一起覆盖。否则极易出现,对象放入HashMap等hash集合后,再去除便取不着的情况。比如

public class PhoneNumber{
private final short areaCode;
private final short prefix;
private final short lineNumber;

public PhoneNumber(int areaCode, int prefix, int lineNumber){
//...set values
}
@Override public boolean equals(Object o){
if(o==this) return true;
if(! (o instanceof PhoneNumber)) return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber ==lineNumber && pn.prefix == prefix && pn.areaCode==areaCode;
}
//No hashCode() overrided!
}

则这时:

Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
m.put(new PhoneNumber(707,867,5309),"Jenny");

这是如果调用

m.get(new PhoneNumber(707,867,5309));
返回的对象会使null。因为hashCode对于put和get方法中的PhoneNumber是不一样的。所以HashMap将第一个对象存入了一个散列桶,而用另一个对象的hashCode从另一个桶中取对象。及时巧合般的2个对象被分在同一个桶中,也会因为hashCode的不同,直接在对象相等验证中失败。

同时,hashCode总是返回同一个或极少量的值也是不行的。因为这样会使一个以hashCode会key的多桶的数据结构退化,成为一个只有一个hashCode的超大的桶,既一个链表。这样这个数据结构的查询效能就冲Log(O)退化为(O)2,这对于一些大的hash集合而言,是影响性能甚至会导致程序不可用的。

书中介绍了一些hashCode生成的范式。也没有太明白,貌似hashCode的实现不仅仅是一个工程学问题,更是一个数学问题,水可以很深。本人道是经常使用apache的HashCodeBuilder来完成hashCode的生成:

@Override
public int hashCode() {
return new HashCodeBuilder(13, 31).append(this.id) .append(this.fieldA).append(this.fieldB).toHashCode();
}
方便又好用。