http://web4.blog.163.com/blog/static/18969413120102291722395/
String的hashCode和equals问题
能否定义两个字符串s1和s2对象,使得以下结论同时成立:
1. s1.eq ls(s2) 得到的结果是false
2. s1.hashCode() == s2.hashCode() 得到的结果是tr
我的第一反应是不存在这样的String对象。因为String重写了eq ls和hashCode。不经过大脑的得出的答案往往是经不住实践的考验的!
实际上,eq ls=tr可以知道hashCode相等,hashcode不相等那么肯定不eq ls。但是hashcode相等却不eq ls的情况是允许出现的。
为了构造一个这样的情况,我们就需要知道String的hashCode是如何实现的:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
其中:
len:字符串的长度
h:初始值为0
val:字符数组,存放的是对应的字符
因此,长度为1的字符串的hashCode也是对应的字符的ascill码,长度为0的字符串的hashCode是0。
因此,要构造两个hashcode相等的字符串转化成为一个解方程问题。
我们直接来看现成的一个结果:
String s1="{~";
String s2="|_";
System.out.println(s1.eq ls(s2)+","+(s1.hashCode()==s2.hashCode()));//false,tr
"{"ascii是123,"~"的ascii是126,"|"的是124,"_"的是95,123*31+126=124*31+95=3939
很显然,hashCode相等,但是却不eq ls。
而楼主还给出了另外一个更有意思的答案:
String s3="\";
String s4="";
System.out.println(s3.eq ls(s4)+","+(s3.hashCode()==s4.hashCode()));//false,tr
这就有点意思了!
首先,我们要弄明白\是一个什么玩意?\u是一个unicode,而jvm对unicode的处理是在编译时就完成了,那么\代表的是哪个字符呢?
反编译一下看看:
ldc #2; //String
也就是说String里面是一个空字符串,那么,s4也是空字符串啊,为什么不eq ls呢?
接着做测试:
System.out.println(s3.length());//1
System.out.println(s4.length());//0
现在看出来了,原来一个是长度为1的字符串,只不过它里面的内容是一个unicode字符,这个字符是一个空字符,一个是长度为0的空字符串,连长度都不一样,肯定不eq ls。这是eq ls的源代码:
public boolean eq ls(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
return false;
}
下面我们来看看hashCode:
System.out.println(s3.hashCode());//0
System.out.println(s4.hashCode());//0
因为他们的内容都是空,根据hashCode的计算方法,hashCode都是0。