解释得很清楚!为什么重写equals()方法必须重写hashcode()方法?

程序猿的内心独白

发布时间:02-2212:02

今天来点比较基础的。

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

posted on 2020-08-28 09:30  w123w  阅读(167)  评论(0编辑  收藏  举报