今天从某大牛博客上看到了这样一个结论(结论完全正确,以下只是说一下本人所思考到的一些知识点以及对初学者来说可能的误区)
问:两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
答:不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;
就是以上这段话,引出了本人的一系列思考。
equals与hashCode的关系
首先equals方法在未被重写的情况下,比较的是两个对象的地址值(其实说地址值也不完全正确,但是可以这么理解)。而hashCode与equals的关系则是:如果两个对象的hashCode相同,它们并不一定相同,但是如果equals相同,那么hashCode一定相同,从而判断他们两个是同一个对象。
可以理解为:现在有两个对象,我们先来看他们的hashCode值是否相同,如果不同则证明不是同一个对象,如果相同在进行equals比较。如果相同才能确定是同一个对象。相当于进行了二次的核对,所以有以上结论如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同,当然具体如何核对中间是一系列复杂的算法。
equals与==的区别
在未被重写的前提下equals与==号的功能其实是一样的,即:比较引用类型的地址值是否相同
@Test
public void equalsTest() {
User u1 = new User();
User u2 = new User();
//比较地址值
System.out.println(u1.equals(u2));//false
//显示出hashCode
System.out.println(u1.hashCode());//两个hashCode值不一样
System.out.println(u2.hashCode());
//比较地址值
System.out.println(u1 == u2);//false
}
从上述代码的结果中可以看出,equals与==返回的都是false。这和我们的结论是相同的。但是为什么我一直在强调未被重写这几个字,并且使用的是自己创建的User对象,就是因为很多初学者在做此demo时,使用了String,从而导致结论与显示结果不一致,从而产生了很多误区。
下面我使用Sting写这个Demo并说明原因
public void hashCodeTest2() {
String s1 = new String("beyondLi");
String s2 = new String("beyondLi");
System.out.println(s1.equals(s2));//true
System.out.println(s1 == s2);//false
/*----------------------------------------------------------------------------------------------------*/
System.out.println("------------------------------------------------------------------------------------");
String s3 = "as";
String s4 = "as";
System.out.println(s3.equals(s4));//true
System.out.println(s3 == s4);//true
}
以上代码的结果有没有出乎你的意料?OK 咱们慢慢分析
知识点1
s1与s2因为是通过new创建出来的,所以s1与s2的地址值是一定不同的。
s3与s4是通过赋值出来的,所以当s3创建后 会将“as”字符串放入到方法区的常量池中,所以s4其实使用的是s3的引用,因此s3与s4的地址值是相同的。
知识点2
不是说equals与==功能相同吗?怎么第五行与第六行的结果不同?而下面两个输出都是true? 那是因为String重写的equals方法(使equals变成了对内容进行对比并返回结果。),而==的功能还是比较的地址值并没有变,又因为s1与s2地址不同而s3与s4地址是相同的,所以出现如上结果,现在我们分别查看s1调用的equlas方法与上例中User调用的equals方法
String(来自String.java)
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;
}
User(来自Object.java)
public boolean equals(Object obj) {
return (this == obj);
}
从源码中也可看出String对equals方法进行了重写。
结论
之所以我们会认为==号比较的是地址值,而equals比较的内容值,是因为我们使用的大部分引用类 例如:String,Integer等都对equals、hashCode进行了重写,从而导致我们会有这样的错觉。
其实如果在未被重写的情况下equals与==号的功能其实是完全一样的。
以上为本人个人理解,如有不足或错误,望指出,共同成长。