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

时间:2009-01-22 11:11:40

标签: c# .net string hash

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

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

然后我将哈希存储到字典中作为到另一个ID的键映射。这很有用所以我不必在默认字典哈希计算期间比较big strings,但我可以按键从字典中删除ID。

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

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

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

12 个答案:

答案 0 :(得分:10)

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

答案 1 :(得分:5)

只是添加一些关于更改哈希码的想法可能来自哪里的细节。

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

String类覆盖object中的默认GetHashCode实现。

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

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

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

  

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

在您的使用中,是的。

  

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

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

  

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

概率非常低,导致哈希在4G域中非常随机。

答案 2 :(得分:4)

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

的更多信息

答案 3 :(得分:4)

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

答案 4 :(得分:4)

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

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

答案 5 :(得分:3)

Object.GetHashCode州的文档

  

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

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

答案 6 :(得分:2)

您不必猜测运行时或版本,只需使用我在业余时间创建的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;
    }
}

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

  

我可以确定哈希值   给定字符串(“非常长的字符串”)   将永远是一样的吗?

  

我可以肯定两种不同   字符串不会有相同的哈希值?

没有

答案 10 :(得分:1)

鉴于存在无数个不同的字符串,它们不可能为每个字符串分配不同的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)。

答案 11 :(得分:-1)

这是过早优化的罪恶的一个很好的例子。

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

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

BTW,存在远远不同的字符串,因此基本逻辑告诉您不可能为每个不同的字符串使用不同的哈希码。