了解为什么重写equals需要重写hashcode?
对于 a==b ,如果a,b为基本数据类型,那么会进行值对比,否则会进行对对象地址的对比。而在顶级父类 Object中的equals方法返回为 return(o == this),完全就是对比两个对象的地址是否相等来判断两个对象是否相等。
而因为对象会被保存在堆内存中,即:即使两个新建对象值完全相同,但地址也一定不同。而常量会被保存在常量池中,在新建一个常量之前,会先判断常量池中是否已经有了相同常量,如果有就把相同常量的地址返回给变量。否则就在常量池中新建一个常量再把地址返回给这个变量。
可通过以下代码验证:
String A = new String("www");
String B = new String("www");
String C ="www";
String D = C;
String E = "www";
E=E.substring(0,1);
System.out.println(A==B);
System.out.println(A.equals(B));
System.out.println(A.hashCode());
System.out.println(B.hashCode());//String 类重写了hashCode()方法
System.out.println(C.hashCode());
System.out.println(E.hashCode());
System.out.println(A==C);
System.out.println(B==C);
System.out.println(D==C);
System.out.println(E==A);
System.out.println(A);
结果为:
false
true
118167
118167
118167
119
false
false
true
false
www
而那么假设String没有重写equals方法,就会导致两个拥有相同字符串的String对象因为地址不同而导致equals返回为false。这样是因为new String(“www”) != new String(“www”)。而从逻辑上来说,两个包含字符串相同的String对象equals应该返回true。
java中的String类是重写了equals的,即对于传入对象,通过看源码可以发现,首先程序会对比两个对象地址是否相等,如果相等直接返回true,如果不相等会判断另一个对比对象是否是String类,如果是的话就会对比两个String对象的值。
在这种情况下,两个String对象即使地址不一样,因为值相同,equals也会返回true。
这时如果不重写hashCode,就会直接调用顶级父类Object的hashCode方法,即对该对象的地址求哈希值。那么会导致两个String对象equals为true,但hashCode却不相等。这样就违背了java中的规定:equals方法的结果必须与hashcode的结果保持一致,即equals为true时,hashcode必须相等。
否则(没有重写hashcode方法时)在java的一些类中,如hashMap,hashTable中,由于将键对值插入时会首先判断两个key对象的哈希值是否相等,以此判断这两个key对象是否相等,这时就会导致会同时插入
hashMap.put(“k”,“v1”)
hashMap.put(“k”:“v2”)
这样通过key来查找就会混乱。
这时即使我们重写了key对象的equals方法,也会导致两个key相等的对象作为多个key插入。
这就是为什么java中重点申明需要保证两个对象如果equals为true,那么两个对象的哈希值也必须一样。
当然,另外,即使两个对象哈希值相等,由于哈希碰撞也可能存在两个对象不相等,所以会继续判定两个对象是否地址相等或者是否equals为true。
由此,总的来说,如果在对象中重写了equals方法,那么必须要重写hashCode方法使得两个equals判定为true的对象,hashCode也必须相等,否则可能会在后续难以预料地出现诸多问题。如hashMap,hashTable,hashSet等等。
一个对象的hashCode是对于该对象的重要标识。hashCode的对比结果必须与equals方法的结果保持一致。