繁体   English   中英

我可以确定给定字符串的内置哈希值始终相同吗?

[英]Can I be sure the built-in hash for a given string is always the same?

我得到这样的字符串哈希:

string content = "a very long string";
int contentHash = content.GetHashCode();

然后我将哈希存储到字典中作为到另一个ID的键映射。 这很有用所以我不必在默认字典哈希计算期间比较大字符串 ,但我可以通过键从字典中获取ID。

我可以确定给定字符串的哈希值(“非常长的字符串”)将始终相同吗?

我可以确定两个不同的字符串不会具有相同的哈希值吗?

另外,如果可能的话,为不同的字符串获取相同的哈希的可能性有多大?

是的,它将是一致的,因为字符串是不可变的。 但是,我认为你在滥用字典。 您应该让字典使用字符串作为键来获取字符串的哈希值。 哈希不保证是唯一的,因此您可以用另一个密钥覆盖一个密钥。

只是添加一些细节,以了解更改哈希码的想法可能来自何处。

正如其他答案正确地说,特定字符串的哈希码对于特定的运行时版本将始终是相同的。 由于性能原因,无法保证较新的运行时可能会使用不同的算法。

String类重写object中的默认GetHashCode实现。

.NET中引用类型的默认实现是分配一个顺序ID(由.NET内部保存)并将其分配给对象(对象堆存储具有用于存储此哈希码的槽,它仅在第一次调用GetHashCode时分配)对于那个对象)。

因此,创建一个类的实例,为其分配一些值然后检索哈希码,然后使用相同的值集执行完全相同的序列将会产生不同的哈希码。 这可能是导致一些人认为哈希码可以改变的原因。 实际上,虽然它是一个类的实例,它被分配了一个哈希码,一旦被分配,哈希码就不会为该实例而改变。

编辑 :我刚刚注意到没有一个答案直接引用你们每个人的问题(尽管我认为答案很明确)但只是为了整理: -

我可以确定给定字符串的哈希值(“非常长的字符串”)将始终相同吗?

在您的使用中,是的。

我可以确定两个不同的字符串不会具有相同的哈希值吗?

不。两个不同的字符串可能具有相同的哈希值

另外,如果可能的话,为不同的字符串获取相同的哈希的可能性有多大?

概率非常低,因此从4G域中得到的散列非常随机。

是的,那就是哈希码的目的! 在运行时的不同版本之间不能保证相同。 有关MSDN的更多信息

正如其他人所指出的,散列将随着时间的推移保持不变。 但是为什么要对字符串进行哈希处理,然后将其作为字典上的键? 哈希不保证是独一无二的。 所以你比较可能是不正确的。 让字典做它的工作。 我认为这个案例最合适的集合是HashSet

正如许多其他人所说,实现依赖于框架的版本,但它也取决于架构 string.GetHashCode()的实现在框架的x86和x64版本中是不同的,即使它们具有相同的版本号。

例如,如果您正在编写客户端/服务器或.net远程处理类型的体系结构,并且希望使用字符串HashCode停止下载大型资源,则只有两者具有相同的版本和位数时才能执行此操作。 否则你应该使用不同的哈希 - MD5,SHA等将正常工作。

Object.GetHashCode的文档说明

如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。

因此,您可以保证给定字符串的哈希码是相同的。 但是,您无法保证它是唯一的(可能有其他字符串具有相同的哈希码)。

您不必猜测运行时或版本,只需使用我在业余时间创建的CaseInsensitiveStringComparer类(您可​​以将其传递给字典的构造函数,或者如果您使用的是.NET 3.5,则为HashSet):

/// <summary>
/// StringComparer that is basically the same as StringComparer.OrdinalIgnoreCase, except that the hash code function is improved and guaranteed not to change.
/// </summary>
public class CaseInsensitiveStringComparer : StringComparer
{
    /// <summary>
    /// Compares two strings, ignoring case
    /// </summary>
    /// <param name="x">First string</param>
    /// <param name="y">Second string</param>
    /// <returns>Compare result</returns>
    public override int Compare(string x, string y)
    {
        return StringComparer.OrdinalIgnoreCase.Compare(x, y);
    }

    /// <summary>
    /// Checks if two strings are equal, ignoring case
    /// </summary>
    /// <param name="x">First string</param>
    /// <param name="y">Second string</param>
    /// <returns>True if strings are equal, false if not</returns>
    public override bool Equals(string x, string y)
    {
        return Compare(x, y) == 0;
    }

    /// <summary>
    /// Gets a hash code for a string, ignoring case
    /// </summary>
    /// <param name="obj">String to get hash code for</param>
    /// <returns>Hash code</returns>
    public override int GetHashCode(string obj)
    {
        if (obj == null)
        {
            return 0;
        }
        int hashCode = 5381;
        char c;
        for (int i = 0; i < obj.Length; i++)
        {
            c = obj[i];
            if (char.IsLower(c))
            {
                c = char.ToUpperInvariant(c);
            }
            hashCode = ((hashCode << 5) + hashCode) + c;
        }
        return hashCode;
    }
}

字符串根据其内容进行哈希处理,因此,如果使用默认的GetHashCode,则哈希应该保持不变。

正如已经提到的,您可以确定参与其中的一个哈希值与它们基于内容进行哈希处理的哈希相同。 但是,您无法确定特定字符串是否会在此处提到的.NET框架的更高版本中进行相同的散列

所以我想说这个方法在内部用于应用程序时很好。 如果要将值保存到数据存储,那么最好自行编写函数以确保它在不同版本之间保持一致。

我可以确定给定字符串的哈希值(“非常长的字符串”)将始终相同吗?

我可以确定两个不同的字符串不会具有相同的哈希值吗?

没有

鉴于存在无限数量的不同字符串,它们不可能为每个字符串分配不同的int(32位,可以表示多达40亿)。

只有8个字符,tehre是2 ^ 60个不同的字符串。 这无限大于2 ^ 32。 当然,其中一些字符串的哈希码必须发生冲突。

具有相同哈希码的两个对象不必相等。 要确定使用equals方法。 这基本上是hashmap用来确定密钥是否相等的策略。

Map.get(String key)

  • 计算密钥的哈希码
  • 使用modulo来确定哪个桶密钥也属于。
  • 循环通过该桶中的所有条目尝试查找匹配的密钥。
  • 找到密钥匹配时返回条目的值。

作为旁注,随着地图获得越来越多的元素,它将重新创建更多存储桶并将所有旧条目放入新存储桶中。 这有助于将存储桶条目列表扩展为非常长的列表。 地图需要许多带有短列表的存储桶。

Object.hashcode的javadoc有趣的阅读 - 我在下面粘贴了一个片段。

 The equals method implements an equivalence relation:

* It is reflexive: for any reference value x, x.equals(x) should return true.
* It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
* It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
* It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified.
* For any non-null reference value x, x.equals(null) should return false. 

类Object的equals方法实现了对象上最具辨别力的等价关系; 也就是说,对于任何参考值x和y,当且仅当x和y引用同一个对象时,此方法才返回true(x == y的值为true)。

这是过早优化的弊端的一个很好的例子。

您是否有分析器或基准测试的输出告诉您同一个散列桶中的条目之间的字符串比较实际上是否会导致性能问题?

不这么认为。 只需将字符串本身用作Dictionary中的键。 这就是你应该如何使用它。

顺便说一句,有不同的字符串远远不同的字符串,所以基本的逻辑告诉你,每个不同的字符串都不可能有不同的哈希码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM