【问题标题】:Hashtable/Dictionary but with key composed of multiple values?哈希表/字典,但键由多个值组成?
【发布时间】:2011-01-22 22:24:07
【问题描述】:

假设我有一个具有 stringProp1、stringProp2 的对象。我希望将 stringProp1、stringProp2 的每个组合存储在字典中。最初我将密钥存储为 key = stringProp1+stringProp2 但这实际上可能会导致错误,具体取决于 2 个值。这个问题的最佳解决方案是创建自定义字典类还是使用内置 .NET 类的更好方法?

【问题讨论】:

    标签: .net dictionary hashtable


    【解决方案1】:

    您使用什么数据结构作为键并不重要,只要键包含比较器正确比较/散列所需的信息即可。

    您甚至可以将您的对象用作字典中的键,并将您喜欢的任何字段与适当的EqualityComparer 实现进行比较。这个使用序数比较比较两个字符串属性:

    class MyObject
    {
        public string StringProp1 { get; set; }
        public string StringProp2 { get; set; }
        public MyObject(string prop1, string prop2)
        {
            StringProp1 = prop1;
            StringProp2 = prop2;
        }
    }
    
    class MyObjectComparerS1S2 : EqualityComparer<MyObject>
    {
        //Change this if you need e.g. case insensitivity or 
        //culture-specific comparisons
        static StringComparer comparer = StringComparer.Ordinal;
    
        public override bool Equals(MyObject x, MyObject y)
        {
            return 
                comparer.Equals(x.StringProp1, y.StringProp1) &&
                comparer.Equals(x.StringProp2, y.StringProp2);
        }
    
        public override int GetHashCode(MyObject obj)
        {
            //Uncomment this if running in a checked context
            //Copycat of Jon Skeet's string hash combining
            //unchecked
            //{
                return 
                    (527 + comparer.GetHashCode(obj.StringProp1)) * 31 +
                    comparer.GetHashCode(obj.StringProp2);
            //}
        }
    
        public static readonly MyObjectComparerS1S2 Instance = 
            new MyObjectComparerS1S2();
    
    }
    
    static void Main(string[] args)
    {
        Dictionary<MyObject, MyObject> dict = 
            new Dictionary<MyObject, MyObject>(MyObjectComparerS1S2.Instance);
        MyObject obj = new MyObject("apple", "plum");
        dict.Add(obj, obj);
        MyObject search = new MyObject("apple", "plum");
        MyObject result = dict[search];
        Console.WriteLine("{0}:{1}", result.StringProp1, result.StringProp2);
    }
    

    您可以通过创建一个虚拟对象、填写字符串键并使用该虚拟对象作为查找键来搜索对象。 如果你不喜欢这个想法,或者不可行,就按照@Vlad 说的做,提取结构或类中的键。在这种情况下,修改比较器以从EqualityComparer&lt;MyKeyStructOrClass&gt; 派生。

    请注意,我使用Jon Skeet's method 来组合字符串哈希。它可能比XOR method found on MSDN 更好。如果你觉得它不够完善,请随意使用另一个哈希实现来处理字符串 - HsiehMurmurBob Jenkin's 或任何你相信的东西。这是一个实际上有一些 C# 代码的 nice page about hash functions也是。

    【讨论】:

      【解决方案2】:

      根据您填充字典的方式和使用它的方式,可以使用匿名类型作为键。例如,如果你有一个班级Person

      public class Person
      {
          public string FirstName { get; set; }
          public string LastName { get; set; }
          public int Age { get; set; }
      }
      

      如果你有这些序列,IEnumerable&lt;Person&gt;,并且想要创建一个将姓名映射到年龄的字典,你可以这样写:

      var personDictionary = people.ToDictionary(p => new { p.FirstName, p.LastName });
      

      这为您提供了一个以名字和姓氏为键的字典,并将整个Person 存储为值。您稍后可以使用以下命令查找密钥:

      personDictionary.TryGetValue(new { FirstName = "John", LastName = "Smith" },
          out person);
      

      如果您尝试在不同的类甚至方法之间传递字典,这对您没有多大帮助,它会变得难以管理,但是对于在单个方法中进行一些快速数据处理,它非常有用。事实上,使用匿名类作为 GroupBy 扩展方法或 group by 查询理解语法的键是很常见的。

      【讨论】:

        【解决方案3】:

        在 .NET 4 中,您可以使用 System.Tuple 作为键。

        var dict = new Dictionary<Tuple<string,string>, int>();
        dict.Add(Tuple.Create("foo","bar"), 1);
        

        【讨论】:

        • 如果您想在早期版本的 .NET 中使用 FSharp.Core,也可以参考它
        • 您也可以在 .Net 3.5 中使用 keyvaluepair 作为新字典>。但是看起来很丑。
        【解决方案4】:

        为什么不直接使用带有 2 个字符串的结构体作为键呢?这将是最简单的。

        【讨论】:

          【解决方案5】:

          如果有一个字符不会出现在任一字符串中,您可以在其中放置一个分隔符。

          例如

          stringProp1 + "|" + stringProp2
          

          如果没有,那么我会推荐Dictionary&lt;string, Dictionary&lt;string, MyValueType&gt;&gt;,如

          var dictionary = new Dictionary<string, Dictionary<string, MyValueType>>();
          // .... Do stuff
          if (!dictionary.ContainsKey(stringProp1))
              dictionary.Add(stringProp1, new Dictionary<string, MyValueType>());
          dictionary[stringProp1][stringProp2] = myValue;
          

          【讨论】:

          • 这行不通。对 "blah|","blah" 和对 "blah","|blah" 怎么样。你需要小心。
          【解决方案6】:

          您可以使用 MD5 算法为每个字符串生成一个值,然后将两个值相加。结果是关键。
          .NET 在 System.Security.Cryptography 命名空间中提供类 MD5CryptoServiceProvider。该类包含计算哈希值的方法 ComputeHash。

          【讨论】:

          • 这不可靠。如果字符串是随机的,则 1/2^128 的概率是 md5 哈希将发生冲突。这将是相当缓慢的。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-08-04
          • 2018-10-25
          • 1970-01-01
          • 1970-01-01
          • 2013-03-21
          • 2012-08-23
          • 2014-01-04
          相关资源
          最近更新 更多