前言
今天还在感概说,可能三年后我的简历和刚毕业那会没啥区别,因为现在已经毕业快一年了,上次重写简历除多了两个项目经验外没啥区别,而简历上面的技术我还需要好久才能掌握。
一、关于hashCode和equals我们知道下面几点
- hashCode相同的两个对象不一定相同
- equals相同的两个对象,hashCode一定相同
- 重写equals一定要重写hashCode
其它的
- == 比较的是两个对象的内存地址值
- equals比较的是两个对象里面的值是否相同,没有重写的equals和==一样
二、hashCode的由来
我们知道hashCode是Object的方法,但是我们点进去可以看到它是一个native方法,也就是不由我们Java去实现,底层是C写的,我们理解默认的hashCode是基于内存地址生成的
public native int hashCode();
三、如何重写hashCode
重写hashCode的基本规则如下:
- 在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
- 当两个对象通过equals()方法比较返回true时,则两个对象的hashCode()方法返回相等的值。
- 对象用作equals()方法比较标准的Field,都应该用来计算hashCode值。
我们根据每一个属性的hashCode,然后加上一定的规则计算出新的hashCode
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getFromUserId() == null) ? 0 : getFromUserId().hashCode());
result = prime * result + ((getFromUserName() == null) ? 0 : getFromUserName().hashCode());
result = prime * result + ((getToUserId() == null) ? 0 : getToUserId().hashCode());
result = prime * result + ((getContent() == null) ? 0 : getContent().hashCode());
result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
result = prime * result + ((getUnReadFlag() == null) ? 0 : getUnReadFlag().hashCode());
return result;
}
四、如何重写equals
- 直接判断两个对象的地址值(用 ==)
- 判断要比较的对象是否为null
- 判断两个对象是否是一个类(比较getClass)
- 调用对象的每个属性的equals进行比较,考虑到对象的属性可能为null,所以我们要先判断是否为null
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
MsgInfo other = (MsgInfo) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getFromUserId() == null ? other.getFromUserId() == null : this.getFromUserId().equals(other.getFromUserId()))
&& (this.getFromUserName() == null ? other.getFromUserName() == null : this.getFromUserName().equals(other.getFromUserName()))
&& (this.getToUserId() == null ? other.getToUserId() == null : this.getToUserId().equals(other.getToUserId()))
&& (this.getContent() == null ? other.getContent() == null : this.getContent().equals(other.getContent()))
&& (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()))
&& (this.getUnReadFlag() == null ? other.getUnReadFlag() == null : this.getUnReadFlag().equals(other.getUnReadFlag()));
}
注:上面的hashCode和equals都是IDEA自动生成的
五、何时需要重写equals和hashCode
我们都知道当我们在使用到HashMap这样的特殊容器的时候需要,但具体为什么需要呢?
有一个面试题是这样的:
Q: 为什么String这样的包装数据类型更适合作为hashMap的key
A:因为String这样的包装数据类型重写的hashCode和equals
事实上我们也基本上是使用String作为hashMap的key,考虑下如果我们使用自定义的类作为key会发生什么呢?
import java.util.HashMap;
import java.util.Map;
public class Test {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static Map<Test,Object> map = new HashMap<>();
static {
Test test = new Test();
test.setName("小道仙");
test.setAge(18);
map.put(test,"哇哈哈");
}
}
思考一下你有什么办法可以获取这个map里面唯一的数据呢?(除去便利,你可以使用 map.get() 方法吗?)
public static void main(String[] args) {
Test test = new Test();
test.setName("小道仙");
test.setAge(18);
System.out.println(map.get(test));
}
上面的这种方式打印出来的是null,在我们没有重写hashCode和equals之前,我们重写new的对象和之前设置进去的对象它们的hashCode和equals永不相同。
所以当我们使用自定义对象作为hashKey的时候就一定要重写hashCode和equals。
六、为什么重写了equals就一定要重写hashCode
因为的hashMap等类似的容器,每次查找都需要进行对比,如果直接使用equals当然可以,但是代价太大了,所以策略是先对比hashCode,hashCode一样再对比equals。
而不重写hashCode,默认的生成和内存地址有关,永不相同。