解释得很清楚!为什么重写equals()方法必须重写hashcode()方法?
今天来点比较基础的。
equals方法是Object类的方法,比较的是地址是否相等,跟==是等价的,注意,这说的是Object中是等价的。
public boolean equals(Object obj) { return (this == obj); }
但是,往往我们会重写这个方法,比如判断字符串是否相等啦
所以String重写了equals()方法:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
可以看到比较的字符串内容是否相等,而不是地址了
再看hashcode方法也是object的方法,是一个native方法,看不到源码,但是根据注释可以知道它是根据对象的地址来算的,那么不同对象的hashcode就不一样了(极大的概率)
那为什么重写equals就要重写hashcode呢?
假设String只重写了equals、没重写hashcode,即 下边的ss1.equals(ss2)是true但是二者的hashcode不相等(地址不同)
String ss1="mayijinfu";
String ss2="mayijinfu";
现在我们在hashset插ss1和ss2,hashset会:
判断插入的值的hashcode存不存在,插ss1判断不存在,把ss1插入
插ss2判断还是不存在(ss1,ss2的hashcode不相等,所以set不知道他两的值一样),这样ss2也被插入了
这时我们遍历set会发现存在两个mayijinfu,这不就乱套了吗。
归根到底 很多容器先根据hashcode判断存不存在如果hashcode存在再去根据equals判断,
因为hashcode大概率不相等,所以可以少判断一步equals
如果先判断equals,大概率是不相等的,还要在判断hashcode
所以我们重写hashcode,不再根据地址来生成hashcode,而是根据我们自己规定的规则,比如上边ss1和ss2,只要内容一样我们就让他的hashcode一样(String就是这么办的)
当我们重写完hashcode后,ss1和ss2的hashcode就相等了,
现在我们在hashset插ss1和ss2,hashset会:
判断插入的值的hashcode存不存在,插ss1时判断这个hashcode不存在,把ss1插入
插ss2判断它的hashcode存不存在,因为我们重写了hashcode,所以已经存在了(ss1,ss2的hashcode相等,所以set知道了两的值可能一样),因为hashcode一样了,set会再去进一步用equals判断,ss1.equals(ss2) 返回true,那确定无疑他两是同一个东西了(在我们的规则下)。
再来一个string的例子:
String的==比的是地址,地址相同才true;
String的equals()比的是每个字符;
String ss1="mayijinfu";
String ss2="mayijinfu";
String sn1= new String("mayijinfu");
String sn2=new String("mayijinfu");
System.out.println("ss1==ss2:"+(ss1==ss2));
System.out.println("ss1==sn1:"+(ss1==sn1));
System.out.println("sn1==sn2:"+(sn1==sn2));
System.out.println("ss1.equals(ss2):"+ss1.equals(ss2));
System.out.println("ss1.equals(sn1):"+ss1.equals(sn1));
System.out.println("sn1.equals(sn2):"+sn1.equals(sn2));
输出结果:
ss1ss2:true
ss1sn1:false
sn1==sn2:false
ss1.equals(ss2):true
ss1.equals(sn1):true
sn1.equals(sn2):true
接下来举一个例子:hashcode相同,但是内容不同 (真的存在)
下边代码中,p1和p2的hashcode相同,但是打印结果为true,false,"no boundaries",null,也就是通过p2得不到p1的value,为啥呢?不是说Hashmap散列的时候看hashcode吗?
计算索引是根据hashcode来算的,但是equals同样要用到的。
String p1="儿女";
String p2 = "农丰";
HashMap<String,String> map = new HashMap<>();
map.put(p1,"no boundaries");
System.out.println("p1.hashCode()==p2.hashCode()"+(p1.hashCode()==p2.hashCode()));
System.out.println("p1.equals(p2)"+p1.equals(p2));
System.out.println("map.get(p1)"+map.get(p1));
System.out.println("map.get(p2)"+map.get(p2));
hashmap的get方法依赖equals方法,上边p1.equals(p2)是false,所以get到的是null啦。所以你想通过p2来get到p1的value,只能重写equals方法,但是问题又来了,String是final类,所以你只能定义别的类了。
先定义一个类pp,然后重写hashcode()和equals()方法,这里hashcode其实就是他们值的hashcode,也就是String里的hashcode,所以"儿女"和"农丰"的hashcode还是一样的,然后我让equals()方法的返回值跟hashcode()的返回值一致。
class pp {
String s;
public pp(String s){
this.s = s;
}
@Override
public boolean equals(Object obj){
return s.hashCode()==obj.hashCode();
}
@Override
public int hashCode(){
return s.hashCode();
}
}
java
pp p1 = new pp("儿女");
pp p2 = new pp("农丰");
HashMap<pp,String> map = new HashMap<>();
map.put(p1,"nb");
System.out.println("p1.hashCode()==p2.hashCode()"+(p1.hashCode()==p2.hashCode()));
System.out.println("p1.equals(p2)"+p1.equals(p2));
System.out.println("map.get(p1)"+map.get(p1));
System.out.println("map.get(p2)"+map.get(p2));
这样的话,p1和p2不光hashcode()相等,equals()也相等了,所以执行map.get(p2)得到的就是p1的value了。打印结果
p1.hashCode()==p2.hashCode()true
p1.equals(p2)true
map.get(p1)no boundaries
map.get(p2)no boundaries
转载:https://baijiahao.baidu.com/s?id=1659208076366376304&wfr=spider&for=pc