为什么String中的equals方法不使用散列?

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()可以快速比较两个string。

更新:我知道,这两个不相等的string,可以有相同的散列。 但是两个相等的string具有相等的散列。 所以,通过使用hashCode(),我们可以立即看到两个string是不相等的。

我只是认为使用hashCode()可以是一个很好的filterequals

更新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; } 

哈希码可能是对不平等的第一轮检查。 但是,它提出了一些权衡。

  1. String哈希码是懒散的计算,虽然他们使用“保护”值。 如果你比较长寿命的string(即,他们可能已经计算了散列码),这不是问题。 否则,当计算散列码(可能很昂贵)或者在散列码还没有被计算时忽略检查的时候,你会遇到困难。 如果你有很多短命的string,你会忽略检查更频繁的比你将使用它。
  2. 在现实世界中,大多数string的前几个字符不同,所以通过首先检查散列码不会节省太多。 当然,也有例外(如URL),但是在现实世界的编程中,它们并不经常出现。

这个问题实际上是由JDK的开发者考虑的。 我在各种消息中找不到为什么没有包括在内。 该增强function也列在错误数据库中 。

也就是说,其中一个build议的变化是:

 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; 

也有讨论使用==为internedstring(即如果两个stringif (this != anotherString) return false;if (this != anotherString) return false; )。

1)计算hashCode可能不会比直接比较string更快。

2)如果hashCode相等,string可能仍然不相等

AFAIK,以下检查可以添加到string。 这检查,如果哈希代码被设置,他们是不同的,那么string不能相等。

 if (hash != 0 && anotherString.hash != 0 && hash != anotherString.hash) return false; if (hash32 != 0 && anotherString.hash32 != 0 && hash32 != anotherString.hash32) return false; 

这对于许多用例来说可能是一个好主意。

然而,作为一个在各种应用中广泛使用的基础类,作者真的不知道这种额外的检查是否可以平均节省或损害性能。

我会猜测大部分的String.equals()都是在HashMap中调用的, 已知哈希码相等之后,所以再次testing哈希码是没有意义的。

如果我们考虑比较2个随机string,即使使用US ASCII这样的小字符集,也很有可能哈希值是不同的,并且第一个字符的char-by-char比较失败。 所以检查哈希将是一个浪费。

正如我所想,hashCode()可以更快地比较两个string。

参数?

反对这个build议的论点:

  • 更多操作

来自String hashcode()必须访问String中的每个字符,并且必须对每个字符进行2计算。
所以我们需要一个有n字符5*n操作(加载,乘法,查找/加载,乘法,存储)的string。 两次,因为我们比较两个string。 (好吧,一个商店和一个负载实际上并不是一个合理的实现。)
对于最好的情况,这对于长度为mnx=min(m,n)两个string总共进行10*x操作。 最差情况是10*xx=m=n 。 平均介于两者之间,也许(m*n)/2

目前等于最佳情况下3操作的实施需求。 2负载, 1比较。 对于长度为mnx=m=n两个string,最差是3*x操作。 平均值介于3*(m*n)/2

  • 即使我们caching哈希值,也不清楚我们保存了什么

我们必须分析使用模式。 可能大多数时候,我们只会问一次平等,而不是多次。 即使我们多次提问,也不足以从caching中节省时间。

不直接反对的速度,但仍然是很好的反驳:

  • 反直觉

我们不期望hashcode等于,因为我们知道hash(a)==hash(b)对于一些a!=b 。 每个人阅读这篇文章(以及有关哈希的知识)都会想知道那里发生了什么。

  • 导致不好的例子/意想不到的行为

我已经可以看到关于SO的下一个问题:“我有一个string,有十亿次”a“,为什么要把它与equal()与”b“比较呢? 🙂

string哈希码不可用,并且是自动的。 为了依靠哈希码,必须对两个string进行计算,然后才能进行比较。 由于碰撞是可能的,如果哈希码相等,则需要第二个逐字符比较。

虽然String对于通常的程序员来说显得是不可变的,但是一旦计算出来,它就有私有字段来存储它的哈希码。 然而这个字段只在hashcode是第一个需要的时候才被计算。 正如你可以从这里的string源代码看到的 :

  private int hash; public int hashCode() { int h = hash; if (h == 0) { ... hash = h; } return h; } 

因此,首先计算哈希码是很有意义的。 对于你的具体情况(也许真正长的string相同的实例相互比较了很多次),它仍然可能是:configuration文件。

如果散列码考虑了string的全部内容,那么计算n个字符的string的散列码需要n个操作。 对于很长的string,这是很多。 如果两个string相同,比较两个string需要n个操作,不能超过计算哈希。 但是,如果string不同,那么很可能会发现很多差异。

string散列函数通常不会考虑所有字符的很长的string。 在这种情况下,如果我比较两个string,我可以首先比较哈希函数使用的字符,至less和检查哈希值一样快。 但是如果这些字符没有差别,那么哈希值将是相同的,所以我必须比较完整的string。

简介:一个好的string比较永远不会比较慢,但通常比比较哈希(并在哈希匹配时比较string)要快得多。