【问题标题】:Comparison via Equals or HashCode. which is faster?通过 Equals 或 HashCode 进行比较。哪个更快?
【发布时间】:2016-06-22 07:48:37
【问题描述】:

我必须将一个对象与同一类的原始属性进行比较。 意思是,我必须比较那些:

struct Identifier
{
    string name;
    string email;
}

使用两个字符串名称和电子邮件。 我知道我可以为姓名和电子邮件创建一个新的标识符实例并将其传递给 equals()。我的应用程序必须非常快速且节省资源。

我知道通过哈希码进行比较不是一个好方法,因为正如 here 所解释的那样,存在冲突。但碰撞对我来说没问题,我只需要它快。

所以,

1) 是通过GetHashCode进行比较(检查两个对象的hashcode是否相同)比Equals()快?

2) 我是否应该为比较创建两个值的 Identifier 的新实例,创建一个直接获取值的新方法?例如

struct Identifier {
  string name;
  string email;

  bool Equals(string name, string email) {
      // todo comparison via hashcode or equals
  }
}

I would use the Equals() and GetHashCode() method generated by resharper.

【问题讨论】:

  • GetHashCode 不是用于相等比较,而是用于获取哈希码。 C# 中的哈希码是 32 位信息,而您的字符串在技术上可能包含无限量的信息。所以完全不同的字符串可能具有相同的哈希码。推荐阅读-stackoverflow.com/questions/371328/…
  • C# 是否缓存字符串的 hashCode?因为如果你必须即时计算它,那将比比较两个字符串要慢。
  • @Thilo No, it doesn't。但是计算两个字符串的哈希码非常快,您绝对应该在自己的GetHashCode 实现中使用它们。
  • 在您的 GetHashCode 实现中:当然。但不需要在 Equals 期间调用它,对吧?
  • 这不是它的工作方式,代码可能使用 GetHashCode() 来更快地进行比较。但是 仍然 需要使用 Equals() 因为哈希码不能是唯一的。

标签: c# performance comparison


【解决方案1】:

如果您将哈希码保存在 Identifier 实例上,比较哈希码可能会更快(见下文)。但是,这与比较是否相等不同。

比较哈希码可以让您检查两个项目是否确实彼此相等:当您获得不同的哈希码时,您就会知道这一点。

但是,当哈希码相等时,您无法对相等做出明确的陈述:项目可能相等或不相等。这就是为什么基于哈希的容器必须始终遵循直接或间接的哈希码比较,以及相等性比较。

尝试像这样实现比较:

struct Identifier {
    string name;
    string email;
    int nameHash;
    int emailHash;
    public Identifier(string name, string email) {
        this.name = name;
        nameHash = name.GetHashCode();
        this.email = email;
        emailHash = email.GetHashCode();
    }
    bool Equals(string name, string email) {
        return name.GetHashCode() == nameHash
            && email.GetHashCode() == emailHash
            && name.equals(this.name)
            && email.equals(this.email);
    }
}

与预先计算的哈希码比较会缩短实际的相等比较,因此当大多数比较最终返回 false 时,您可以节省一些 CPU 周期。

【讨论】:

  • 为什么计算两个哈希码比比较两个字符串更快?两者似乎都必须遍历字符串(并且比较可能会在第一个不匹配的字符上短路)。
  • @Thilo 确实并不快。要查看 2 个对象是否相等,只需调用 Equals 方法即可。 GetHashCode 仅用于在哈希集合(HashSet、Dictionary...)中“排序”(有点)您的对象,以便能够以 O(1) 复杂度找到它。
  • @Thilo 我确信 C# 会像 Java 那样缓存字符串哈希码,但快速查看源代码告诉我我错了:.NET 设计人员追求节省内存。计算哈希码在 CPU 缓存方面略有优势,因为代码从顺序位置读取,因此大多数读取将是缓存命中。但是,这仅适用于非常长的字符串,即使那样影响也很小。无论如何,我改变了答案的措辞,并提出了一个明确缓存哈希码的实现。
【解决方案2】:

是通过GetHashCode进行比较(检查两个对象的hashcode 是否相同)比 Equals() 快?

您似乎混淆了这两个概念。 GetHashCode 的目的不是寻求两个对象实例之间的相等性,它的存​​在只是为了让每个对象都可以轻松地为可能在其上中继的任何外部资源提供哈希码值。

另一方面,Equals 用于确定相等性。应该是两个产生true for equals的方法,提供相同的哈希码,而不是相反。

The documentation on object.GetHashCode 提供了一个很好的解释:

两个相等的对象返回相等的哈希码。然而, 反过来是不正确的:相等的哈希码并不意味着对象 相等,因为不同的(不相等的)对象可以有相同的哈希 代码。此外,.NET Framework 不保证默认 GetHashCode 方法的实现,以及此方法的值 .NET Framework 版本和平台之间的返回值可能不同,例如 作为 32 位和 64 位平台。由于这些原因,请勿使用 此方法的默认实现作为唯一的对象标识符 用于散列目的。由此产生两个后果:

  • 您不应该假设相等的哈希码意味着对象相等。
  • 您不应该在应用程序域之外保留或使用哈希码 它是创建的,因为同一个对象可能会散列 应用领域、流程和平台。

如果你想检查两个实例之间是否相等,我绝对推荐实现IEquatable<T> 并覆盖object.GetHashCode

附带说明 - 我看到您使用的是 struct。您应该注意 struct 在 C# 中的语义与在 C++ 或 C 中的语义不同,我希望您了解它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多