.NET Hashtable - “相同”键,不同的哈希值

时间:2010-03-08 21:48:21

标签: .net mono hashtable castle-monorail brail

两个.net字符串是否可能有不同的哈希值?我有一个Hashtable,其中包括关键的“路径”。当我遍历表格中的元素进行打印时,我可以看到密钥存在。

然而,当试图查找它时,没有匹配的元素。调试表明我正在寻找的字符串与我提供的字符串的哈希值不同。

此代码位于Castle Monorail项目中,使用brail作为视图引擎。我正在寻找的关键是通过这样的辫子线插入:

UrlHelper.Link(node.CurrentPage.LinkText, {@params: {@path: "/Page1"}})

然后,在这个方法中(在自定义IRoutingRule中):

public string CreateUrl(System.Collections.IDictionary parameters)
{
    PrintDictionaryToLog(parameters);
    string url;
    if (parameters.Contains("path")) {
        url = (string)parameters["path"];
    }
    else {
        return null;
    }
}

密钥将打印到日志中,但该函数返回null。我不知道这甚至可能是.net字符串的问题,但我想这是某种编码问题?

哦,这是单声道运行。

根据要求,以下是日志中的相关行:

2010-03-08 22:58:00,504 [7] DEBUG Knickle.Framework.Routing.PageRoute (null) - Parameters: {System.String controller=null, System.String path=Page1, System.String path=/Page1, System.String action=null, System.String area=null}

此外,我在日志打印调用上面添加了一行代码:

parameters.Add("path", "Page1");

查看日志,您会注意到有两个“路径”键。调试器在表中的不同位置显示两个键。

6 个答案:

答案 0 :(得分:2)

以下是GetHashCode for Strings上MSDN的链接。如果它们相等,则哈希码应该匹配,但是如果它们不相等,它们仍然可以具有相同的哈希值(尽管可能性很小)。

http://msdn.microsoft.com/en-us/library/system.string.gethashcode.aspx

来自文章:

  

如果两个字符串对象相等,则   GetHashCode方法返回相同的内容   值。但是,没有一个独特的   每个唯一字符串的哈希码值   值。不同的字符串可以返回   相同的哈希码。

答案 1 :(得分:1)

不应该发生。检查尾随空格,URL转义等。

答案 2 :(得分:1)

根据StringComparison.Ordinal或更简单地String.Equals等于的字符串在所有情况下都将具有相同的哈希码。

答案 3 :(得分:1)

这就是这条线[Castle.MonoRail.Framework.Services.DefaultUrlBuilder:397] ......

// Forces copying entries to a non readonly dictionary, preserving the original one
parameters = new Hashtable(parameters, StringComparer.InvariantCultureIgnoreCase);

如果IEqualityComparer被删除,问题就会消失。

如果这是一个单声道的bug(我认为它必须是),它仍然是单声道2.10.8.1(Debian 2.10.8.1-5ubuntu1)的问题。

测试用例需要编写和归档。

答案 4 :(得分:0)

您使用的是Mono的哪个版本?它可能是Mono中的一个错误,如果是这样的话,欢迎错误报告。

但是我同意Seva,可能原因是一些尾随空格或分隔符或其他编码问题导致字符串以某种微妙的方式不同并导致不同的哈希值。

答案 5 :(得分:-1)

Hashtable优化查找。它计算您添加的每个键的哈希值。然后,它使用此哈希代码非常快速地查找元素。它是较旧的.NET Framework类型。它比通用的Dictionary类型慢。

词典 实施例

首先,您可以使用最简单的构造函数创建新的Hashtable。创建时,Hashtable没有值。我们直接使用索引器赋值,该索引器使用方括号[]。

下一步: 该示例将三个整数键添加到Hashtable对象中,每个键都有一个字符串值。

将条目添加到Hashtable并显示它们[C#]

using System;
using System.Collections;

class Program
{
    static void Main()
    {
    Hashtable hashtable = new Hashtable();
    hashtable[1] = "One";
    hashtable[2] = "Two";
    hashtable[13] = "Thirteen";

    foreach (DictionaryEntry entry in hashtable)
    {
        Console.WriteLine("{0}, {1}", entry.Key, entry.Value);
    }
    }
}

结果

13, Thirteen
2, Two
1, One

程序显示从foreach循环中的枚举器返回的所有DictionaryEntry对象。 WriteLine调用包含一个格式字符串,用逗号显示键/值对。

<强> FOREACH

您可以在foreach循环中使用DictionaryEntry类型遍历Hashtables。您也可以获取Keys集合并将其复制到ArrayList中。 DictionaryEntry包含两个对象:键和值。

<强> FOREACH 的DictionaryEntry 包含 接下来我们将看到Hashtable上一些最常见和最重要的实例方法。您将需要使用密钥内容在Hashtable上调用ContainsKey。如果找到键,则此方法返回true,无论值是什么。

同时 包含的工作方式相同。我们看到了使用带有[]方括号的索引器的示例。

使用包含方法[C#]

的程序
using System;
using System.Collections;

class Program
{
    static Hashtable GetHashtable()
    {
    // Create and return new Hashtable.
    Hashtable hashtable = new Hashtable();
    hashtable.Add("Area", 1000);
    hashtable.Add("Perimeter", 55);
    hashtable.Add("Mortgage", 540);
    return hashtable;
    }

    static void Main()
    {
    Hashtable hashtable = GetHashtable();

    // See if the Hashtable contains this key.
    Console.WriteLine(hashtable.ContainsKey("Perimeter"));

    // Test the Contains method. It works the same way.
    Console.WriteLine(hashtable.Contains("Area"));

    // Get value of Area with indexer.
    int value = (int)hashtable["Area"];

    // Write the value of Area.
    Console.WriteLine(value);
    }
}

<强>输出

True
True
1000

索引器是一个在方括号内接收参数的属性。 Hashtable实现了索引器。它返回普通对象,因此您必须强制转换它们。有关详细信息,请参阅铸造部分。

<强>索引 多种类型 接下来,我们向Hashtable添加多个类型。这里的示例添加了字符串键和int键。每个键/值对具有不同的类型。您可以将它们全部放在同一个Hashtable中。

使用多种类型的程序[C#]

using System;
using System.Collections;

class Program
{
    static Hashtable GetHashtable()
    {
    Hashtable hashtable = new Hashtable();

    hashtable.Add(300, "Carrot");
    hashtable.Add("Area", 1000);
    return hashtable;
    }

    static void Main()
    {
    Hashtable hashtable = GetHashtable();

    string value1 = (string)hashtable[300];
    Console.WriteLine(value1);

    int value2 = (int)hashtable["Area"];
    Console.WriteLine(value2);
    }
}

<强>输出

Carrot
1000

此代码可能会抛出异常。铸造是一项微妙的操作。如果强制转换应用于其他类型,则该语句可能会抛出InvalidCastException。您可以使用is或as-statements来避免这种情况。

<强>角色 您可以避免使用Hashtable抛出问题。您可以使用as-operator尝试将对象强制转换为特定的引用类型。如果强制转换不成功,结果将为null。

提示: 您也可以使用is-operator。此运算符根据结果返回true或false。

时 如 强制转换Hashtable值的程序[C#]

using System;
using System.Collections;
using System.IO;

class Program
{
    static void Main()
    {
    Hashtable hashtable = new Hashtable();
    hashtable.Add(400, "Blazer");

    // This cast will succeed.
    string value = hashtable[400] as string;
    if (value != null)
    {
        Console.WriteLine(value);
    }

    // This cast won't succeed, but won't throw.
    StreamReader reader = hashtable[400] as StreamReader;
    if (reader != null)
    {
        Console.WriteLine("Unexpected");
    }

    // You can get the object and test it.
    object value2 = hashtable[400];
    if (value2 is string)
    {
        Console.Write("is string: ");
        Console.WriteLine(value2);
    }
    }
}

<强>输出

外套 是字符串:Blazer

使用Hashtable,您可以使用as-operator减少强制转换次数并提高性能。这是微软静态分析工具FxCop提供的一个性能警告。

<强>的FxCop * 键,值 * 我们接下来获取所有键或值。我们可以遍历这些值,或将它们存储在单独的ArrayList集合中。此示例显示所有键,然后显示所有值,然后将键存储在ArrayList中。

注意: 此C#Hashtable示例使用Keys属性。此属性返回所有键。

循环键,值并存储在ArrayList [C#]

using System;
using System.Collections;

class Program
{
    static void Main()
    {
    Hashtable hashtable = new Hashtable();
    hashtable.Add(400, "Blaze");
    hashtable.Add(500, "Fiery");
    hashtable.Add(600, "Fire");
    hashtable.Add(800, "Immolate");

    // Display the keys.
    foreach (int key in hashtable.Keys)
    {
        Console.WriteLine(key);
    }

    // Display the values.
    foreach (string value in hashtable.Values)
    {
        Console.WriteLine(value);
    }

    // Put keys in an ArrayList.
    ArrayList arrayList = new ArrayList(hashtable.Keys);
    foreach (int key in arrayList)
    {
        Console.WriteLine(key);
    }
    }
}

输出

800       (First loop)
600
500
400
Immolate  (Second loop)
Fire
Fiery
Blaze
800       (Third loop)
600
500
400

循环键。程序中的第一个循环遍历Hashtable实例上的Keys实例属性返回的集合。您可以使用foreach循环获得最简单的语法。

循环使用值。程序中的第二个循环显示如何仅枚举Hashtable实例中的值。存储在Hashtable中的四个单词将被打印到屏幕上。

<强> Console.WriteLine 存储在ArrayList中。该示例使用复制构造函数创建一个新的ArrayList,并将Keys(或Values)属性作为参数传递给它。在执行ArrayList构造函数之后,可以枚举ArrayList。

<强>的ArrayList

提示: Keys和Values公共访问器在访问Hashtable时返回密钥和值的集合。

<强>然而: 如果需要成对查看所有键和值,最好枚举Hashtable实例本身。

计数,清除

您可以使用Count属性计算Hashtable中的元素。该示例还显示使用Clear方法擦除所有Hashtable内容。另一种方法是将Hashtable引用重新分配给新的Hashtable()。

注意: 此示例显示如何使用Count属性。此属性返回元素数。

使用Count on Hashtable [C#]

的程序
using System;
using System.Collections;

class Program
{
    static void Main()
    {
    // Add four elements to Hashtable.
    Hashtable hashtable = new Hashtable();
    hashtable.Add(1, "Sandy");
    hashtable.Add(2, "Bruce");
    hashtable.Add(3, "Fourth");
    hashtable.Add(10, "July");

    // Get Count of Hashtable.
    int count = hashtable.Count;
    Console.WriteLine(count);

    // Clear the Hashtable.
    hashtable.Clear();

    // Get Count of Hashtable again.
    Console.WriteLine(hashtable.Count);
    }
}

输出

4
0

首先,程序将四个带有四个值的键添加到Hashtable实例中。然后它捕获Count,即4,然后在Hashtable上使用Clear,它现在有0个元素。 Hashtable为空但不为空。

在Hashtable上计算属性。 Hashtable类实现一个公共实例属性访问器,它返回Hashtable中的元素数。 Count属性不执行冗长的计算或循环。

注意: MSDN声明,对于Count,&#34;检索此属性的值是O(1)操作。&#34;

恒定时间。此属性是一个恒定时间访问器。它将报告Hashtable在其桶中具有多个实际元素(或零)。它返回一个整数,是一个资源需求低的简单访问器。

<强>基准

继续,我们针对System.Collections.Generic命名空间中的Dictionary集合测试System.Collections命名空间中的Hashtable集合。基准测试首先填充每个集合的等效版本。

,然后: 它测试一个找到的密钥和一个未找到的密钥。它重复了这2000万次。

基准[C#]

中使用的哈希表
Hashtable hashtable = new Hashtable();
for (int i = 0; i < 10000; i++)
{
    hashtable[i.ToString("00000")] = i;
}

Dictionary used in benchmark [C#]

var dictionary = new Dictionary<string, int>();
for (int i = 0; i < 10000; i++)
{
    dictionary.Add(i.ToString("00000"), i);
}

Statements benchmarked [C#]

hashtable.ContainsKey("09999")
hashtable.ContainsKey("30000")

dictionary.ContainsKey("09999")
dictionary.ContainsKey("30000")

2000万次查找的基准

Hashtable result:  966 ms
Dictionary result: 673 ms

Hashtable代码明显慢于Dictionary代码。我估计这里的Hashtable慢了30%。这意味着对于强类型集合,Dictionary更快。

<强>构造

Hashtable类中有15个重载的构造函数。这些提供了指定容量的方法。它们允许您将现有集合复制到Hashtable中。您还可以指定如何计算哈希码。

构造函数提示 摘要

我们使用了Hashtable集合。这是一个较旧的集合,已被Dictionary集合废弃。在维护旧程序时,了解如何使用它至关重要。这些计划对许多组织都很重要。