String类中的equals
方法的代码是
public boolean equals(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()?
据我所知,hashCode()可以快速比较两个字符串。
更新:我知道,两个不相等的字符串,可以有相同的哈希值。但两个相等的字符串具有相等的哈希值。因此,通过使用hashCode(),我们可以立即看到两个字符串不相等。
我只是认为在equals
中使用hashCode()可以是一个很好的过滤器。
更新2:这里有一些代码,我们在这里谈论。
这是一个String方法等于
的示例public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
if (hashCode() == anotherString.hashCode()){
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;
}
}else{
return false;
}
}
return false;
}
答案 0 :(得分:39)
Hashcode可能是对不平等的第一轮检查。但是,它提出了一些权衡。
String
哈希码是懒惰计算的,尽管它们使用“保护”值。如果你要比较长寿命的字符串(即,他们可能已经计算了哈希码),这不是问题。否则,您将无法计算哈希码(可能很昂贵)或者在尚未计算哈希码时忽略检查。如果你有很多短命的字符串,你会比你使用它更频繁地忽略支票。答案 1 :(得分:14)
JDK的开发人员实际上已经考虑过这个问题。我在various messages中找不到它为什么没有被包括在内。此增强功能也列在the bug database。
中即,建议的更改之一是:
public boolean equals(Object anObject) {
if (this == anObject) // 1st check identitiy
return true;
if (anObject instanceof String) { // 2nd check type
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) { // 3rd check lengths
if (n != 0) { // 4th avoid loading registers from members if length == 0
int h1 = hash, h2 = anotherString.hash;
if (h1 != 0 && h2 != 0 && h1 != h2) // 5th check the hashes
return false;
还讨论了将==
用于实习字符串(即,如果两个字符串都被实习:if (this != anotherString) return false;
)。
答案 2 :(得分:7)
1)计算hashCode可能不会比直接比较字符串更快。
2)如果hashCode相等,则字符串可能仍不相等
答案 3 :(得分:4)
对于许多用例来说,这可能是一个好主意。
然而,作为一个广泛用于各种应用程序的基础类,作者真的不知道这个额外的检查是否可以平均保存或损害性能。
我猜测大多数String.equals()
都是在Hashmap中调用的,在之后已知哈希码相等,因此再次测试哈希码是毫无意义的。 / p>
如果我们考虑比较2个随机字符串,即使使用像US ASCII这样的小字符集,很可能哈希值不同,并且char-by-char比较在第1个char上失败。因此检查哈希值是浪费。
答案 4 :(得分:2)
AFAIK,可以在String中添加以下检查。这检查如果设置了哈希码并且它们不同,则字符串不能相等。
if (hash != 0 && anotherString.hash != 0 && hash != anotherString.hash)
return false;
if (hash32 != 0 && anotherString.hash32 != 0 && hash32 != anotherString.hash32)
return false;
答案 5 :(得分:0)
字符串哈希码不可免费且自动使用。为了依赖哈希码,必须为两个字符串计算它,然后才能进行比较。由于冲突是可能的,如果哈希码相等,则需要进行第二次char-by-char比较。
虽然String对于通常的程序员来说是不可变的,但是一旦计算出来,它确实有私有字段来存储它的哈希码。但是,只有在首次需要哈希码时才会计算此字段。正如您可以从字符串源代码here中看到的那样:
private int hash;
public int hashCode() {
int h = hash;
if (h == 0) {
...
hash = h;
}
return h;
}
因此,首先计算哈希码是有意义的并不明显。对于你的具体情况(也许真正长字符串的相同实例很多次相互比较),它仍然可能是:profile。
答案 6 :(得分:0)
我认为,hashCode()可以更快地比较两个字符串。
参数?
反对此提案的论点:
hashcode()
的 String
必须访问字符串中的每个字符,并且必须对每个字符进行2
计算。
所以我们需要一个包含n
个字符5*n
操作的字符串(加载,乘法,查找/加载,乘法,存储)。两次,因为我们比较两个字符串。 (好吧,一个商店和一个负载在合理的实施中并不合适。)
对于最佳情况,这会对长度为10*x
和m
以及n
的两个字符串进行总计x=min(m,n)
次操作。最坏的情况是10*x
x=m=n
。平均值介于两者之间,可能是(m*n)/2
。
在最佳情况3
操作中,当前等于实现需求。 2
次加载,1
比较。对于长度为3*x
和m
以及n
的两个字符串,最差x=m=n
次操作。平均值介于两者之间,可能是3*(m*n)/2
。
我们必须分析使用模式。可能在大多数情况下,我们只会问一次等于,而不是多次。即使我们多次询问,也不足以节省缓存时间。
不是直接反对速度,但仍然是好的反驳:
我们不希望哈希代码等于,因为我们肯定知道某些hash(a)==hash(b)
的{{1}}。阅读本文的每个人(以及关于哈希的知识)都会想知道那里发生了什么。
我已经可以看到关于SO的下一个问题了:“我有一个字符串有几十亿次'a'。为什么要将它与equal()与'b'进行比较需要永远?” :)
答案 7 :(得分:0)
如果哈希码考虑了字符串的全部内容,那么计算具有n个字符的字符串的哈希码需要n次操作。对于很长的字符串来说。比较两个字符串如果它们相同则需要n次操作,而不是计算散列。但如果字符串不同,那么很可能早就发现了差异。
字符串哈希函数通常不考虑非常长字符串的所有字符。在这种情况下,如果我比较两个字符串,我可以先比较哈希函数使用的字符,并且我至少和检查哈希值一样快。但是如果这些字符没有区别,那么哈希值将是相同的,所以我必须比较完整的字符串。
总结:一个好的字符串比较永远不会慢,但通常比比较哈希(并在哈希匹配时比较字符串)要快得多。