你有没有重写过hasCode()和equals()方法?
2019-10-10 本文已影响0人
yunqing_71
先来写一个简单的例子:
/**
* 自定义一个钥匙类
*/
@Getter
@Setter
@AllArgsConstructor
class Key{
private Integer id;
private String name;
}
@Test
public void test4() {
Key k1 = new Key(1, "锁A的钥匙1");
Key k2 = new Key(1, "锁A的钥匙2");
Map<Key, String> map = new HashMap<>();
map.put(k1, "我能打开锁A");
System.out.println(map.get(k2));
}
如上面代码所示:实例化两把钥匙,这两把钥匙都是能打开锁A的钥匙,从实际角度出发,以上程序好比我用钥匙k1来锁门,然后用钥匙k2来打开门,这是符合逻辑的。但是输出结果却不是 我能打开锁A,而是null
仔细分析,原因有两个:一是没有重写hasCode方法,二是没有重写equal方法
当我们把k1 放入hashmap中的时候,首先会调用Key类的hascode方法计算hash值,然后把k1放到hash值所指引的内存位置。但是Key类中并没有重写hashCode方法,所以会调用Object类的hasCode方法,所以在输出的时候,取k2的value值得时候,也会走Object的hasCode(),因为k2和k1是两个不同的对象,所以内存地址肯定不一样,所以用k2的hash值肯定获取不到k1的value.
接下来在Key类上重写hasCode()
@Getter
@Setter
@AllArgsConstructor
class Key{
private Integer id;
private String name;
/**
* 重写hasCode,根据key的id进行hash计算,确保k1和k2存放在同一个内存地址
* @return
*/
@Override
public int hashCode() {
return id.hashCode();
}
}
上面的代码我们重写了hasCode()但是输出的结果还是null,
这是因为还没有重写equals()方法,因为hashMap是用链地址法处理哈希冲突的,所以在这个内存位置,可能存在多个链表形式存储的对象,例如锁B的钥匙经过hash计算也应该放在这个内存地址,所以虽然通过k2能够找到这个位置的k1但是并不能确定k1就是和k2能打开同一把锁,因为这里会调用Object的equals方法去判断,所以k1和k2肯定不相等。这就需要重写equals认为只要两个对象都是Key类型,并且id相等,就认为他们相等;
@Getter
@Setter
@AllArgsConstructor
class Key{
private Integer id;
private String name;
/**
* 重写hasCode,确保k1和k2存放在同一个内存地址
* @return
*/
@Override
public int hashCode() {
return id.hashCode();
}
/**
* 如果不重写equals方法,可能两把不同锁的钥匙有同一个hasCode值而已,并不确定这两把钥匙是同一个锁的
* 所以要重写equals方法判断是否是同一个锁的钥匙
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Key)) {
return false;
}else {
/**
* 重写方法规定id相等就是同一把锁的钥匙
*/
return this.getId().equals(((Key) obj).getId());
}
}
}
重写equals方法之后,就可以得到相应的值了,输出 “我能打开锁A”