Effective Java代码规则之十一:覆盖equals时总要覆盖hashCode

1、解释

为什么要覆盖hashCode

在每个覆盖了equals方法的类中, 都必须覆盖hashCode方法。如果不这样做的话,就会违反hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这类集合包括HashMap和HashSet ,举个例子:

private String loginId;
private String password;
private String name;
public UserBean(String loginId, String password, String name) {
this.loginId = loginId;
this.password = password;
this.name = name;
}
public static void main(String[] args) {
Map<userbean> m = new HashMap<>();
UserBean u1 = new UserBean("111", "111", "stanley");
UserBean u2 = new UserBean("111", "111", "stanley");
m.put(u1, "stanley");
System.out.println("u1.equals(u2): " + u1.equals(u2));
System.out.println("u2.equals(u1): " + u2.equals(u1));
System.out.println("get u1 from hashmap: " + m.get(u1));
System.out.println("get u2 from hashmap: " + m.get(u2));
}
/<userbean>

该类覆盖了equals方法,但是没有覆盖hashCode方法,执行结果如下:

u1.equals(u2): true
u2.equals(u1): true
get u1 from hashmap: stanley
get u2 from hashmap: null

因为UserBean类没有覆盖hashcode方法,从而导致两个相等的实例具有不相等的散列码。

hashCode的通用约定:

在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有改变,那么对于这同一个对象调用多次hashcode都必须返回同一个整数。

如果两个对象相等(用equals(Object)方法比较),那么这两个对象的hashCode值也必须相等。

如果两个对象不相等(用equals(Object)方法比较),那么这两个对象的hashCode值不一定不相等(最好不相等)。

2、实现高质量hashCode的方法

2.1、声明一个int变量并命名为esult,将它初始化为对象中第一个关键域的散列码

2.2、为对象计算int类型的散列码c

如果该域是基本类型,则计算Type.hashCode(f),这里的Type 是装箱基本类型的类,与f的类型相对应如果该域是一个对象引用,则同样为这个域递归地调用hashCode如果该域是一个数组,可以使用Arrays.hashCode方法

2.3、按照下面的公式,把步骤 2.2中计算得到的散列码c合并到result中

result = 31 * result + c;

这里提个问题:为什么选择数字31作为生成hashCode值的乘数?

3、最佳实践

在实际工作中,覆盖hashCode方法比较麻烦,通常我们使用IDE工具自带的生成功能:

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((loginId == null) ? 0 : loginId.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((password == null) ? 0 : password.hashCode());
return result;
}

专业从事软件研发工作多年,在软件设计、开发、测试、研发管理等领域里经验丰富,感兴趣的朋友可以关注我的头条号,相信一定会有所收获。

如果有软件研发方面的问题,可以咨询我。

谢谢!