代码之家  ›  专栏  ›  技术社区  ›  Josh G

选择一个好的字典键

  •  2
  • Josh G  · 技术社区  · 17 年前

    我有一个要用来查找其他对象的对象。我将使用 Dictionary<TKey, TValue>() .

    key对象有两个唯一标识它的字符串,比如 KeyObj.Str1 KeyObj.Str2 .

    你建议我用什么作为字典的键?

    1:字符串的串联。

    Dictionary<String, TValue>();
    Key = KeyObj.Str1:KeyObj.Str2; ("somestring:anotherstring")
    

    2:每个对象都有一个唯一的整数来标识它?

    Dictionary<int, TValue>();
    KeyObj.ID = _nextID++;
    Key = KeyObj.ID;
    

    3:对对象的引用。

    Dictionary<KeyObj, TValue>();
    Key = KeyObj;
    

    选项3将是最简单的,但它似乎是低效的索引字典的基础上的参考值。

    如果key对象包含一个唯一的字符串,那么显而易见的选择是使用这个字符串,但是只有两个字符串在组合中是唯一的,这会使它更加困难。

    10 回复  |  直到 12 年前
        1
  •  2
  •   vgru    17 年前

    串接的字符串应该工作得最好。

    如果您知道它们的组合是唯一的,那么这就是您应该选择的——记住哈希代码是 通常 独一无二,但并非总是如此。

        2
  •  2
  •   Benjamin Cutler    17 年前

    如果可以适当地重写getHashCode()和equals(),也就是说,可以使用选项3:

        public override int GetHashCode()
        {
            return str1.GetHashCode() ^ str2.GetHashCode();
        }
    
        public override bool Equals(object obj)
        {
            if (!obj is KeyObj)
            {
                return false;
            }
    
            KeyObj key = (KeyObj)obj;
            return this.str1.Equals(key.str1) && this.str2.Equals(key.str2);
        }
    
        3
  •  1
  •   Matt Grande    17 年前

    我会说选项1。

        4
  •  1
  •   Oscar Cabrero    17 年前

    使用keyobj.gethashcode()怎么样?

        5
  •  1
  •   Chris Doggett    17 年前

    它们中的任何一个都是有效的,但我假设您希望能够基于两个字符串中的一个快速找到这些对象,因此使用int作为键意味着您仍然需要扫描这些值以找到所需的对象。

    字符串是否都是唯一的,或者只有在组合时才是唯一的?如果它们都是独一无二的,而且你愿意交换一点空间,你可以这样做:

    dict.Add(KeyObj.Str1, KeyObj);
    dict.Add(KeyObj.Str2, KeyObj);
    

    并且有两个对字典中的对象的引用,使用每个唯一的字符串作为键。或者,如果字符串只在一起是唯一的,则可以将它们组合在一起,并在内部使用哈希代码来查找它们。

        6
  •  1
  •   John Rasch    17 年前

    将它们串联起来可能是最好的主意。可以在中公开属性 KeyObj 对象,这样您就不必每次访问字典值时都执行它。

    编辑:

    我显然误解了这个问题。我认为你真正想做的是1和3的混合,你可以覆盖 Equals() GetHashCode() 使用 string 唯一标识对象的(只需确保它们是不可变的!)

    public override Equals(object obj) 
    {
       if (obj == null || !(obj is KeyObj))
          return false;
       KeyObj other = (KeyObj)obj;
       if (this.Key1 == other.Key1 && this.Key2 == other.Key2)
         return true;
       return false;
    }
    
    public override GetHashCode()
    {
        return (this.Key1 + this.Key2).GetHashCode();
    }
    

    然后您可以使用建议的第三个选项:

    Dictionary<KeyObj, ValueObj>...
    
        7
  •  0
  •   Ian    17 年前

    您不需要使用新类作为字典键。使用一个新的结构,因为它会更轻…显然,它由这两个字符串值组成。

        8
  •  0
  •   Community Mohan Dere    9 年前

    如果性能是主要考虑因素,可以考虑使用两个字符串的哈希值。但是“value”字段必须同时包含键和值。

    我提到了另一个这样的问题,我只需要找到它。

    Is it faster to search for a large string in a DB by its hashcode?

    但这个问题更倾向于数据库。性能被考虑数千次迭代。

        9
  •  0
  •   Walden Leverich    17 年前

    请记住,字典是一个美化的哈希表,因此键(没有双关语)是使用一个键,这将导致很少(如果有的话)与另一个键发生冲突。我倾向于3,但这是假设keyobj类型有一个好的散列值生成器。

        10
  •  0
  •   SamXie    12 年前

    字符串作为键是最好的,请参阅我的测试代码:

    var tuplekeydict=新字典,字符串>();

            for (int i = 0; i < 1000000; i++)
            {
                tupleKeyDict.Add(new Tuple<int, int>(i,0),i.ToString() );
            }
    
            System.Diagnostics.Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();
            string e1 = tupleKeyDict[new Tuple<int, int>(0, 0)];
            string e2 = tupleKeyDict[new Tuple<int, int>(500000, 0)];
            string e3 = tupleKeyDict[new Tuple<int, int>(999999, 0)];
            stopWatch.Stop();
            Console.WriteLine("Tuplekey cost(tick): " + stopWatch.ElapsedTicks.ToString());
            Console.WriteLine("Tuplekey cost(ms): " + stopWatch.ElapsedMilliseconds.ToString());
    
    
    
    
    
            var strKeyDict = new Dictionary<string, string>();
    
            for (int i = 0; i < 1000000; i++)
            {
                strKeyDict.Add(i.ToString() + ":0", i.ToString());
            }
    
            System.Diagnostics.Stopwatch stopWatch2 = new Stopwatch();
            stopWatch2.Start();
            string se1 = strKeyDict["0:0"];
            string se2 = strKeyDict["500000:0"];
            string se3 = strKeyDict["999999:0"];
            stopWatch2.Stop();
            Console.WriteLine("strkey cost(tick): " + stopWatch2.ElapsedTicks.ToString());
            Console.WriteLine("strkey cost(ms): " + stopWatch2.ElapsedMilliseconds.ToString());
    
    
    
    
            var intKeyDict = new Dictionary<int, string>();
    
            for (int i = 0; i < 1000000; i++)
            {
                intKeyDict.Add(i, i.ToString());
            }
    
            System.Diagnostics.Stopwatch stopWatch3 = new Stopwatch();
            stopWatch3.Start();
            string ie1 = intKeyDict[0];
            string ie2 = intKeyDict[500000];
            string ie3 = intKeyDict[999999];
            stopWatch3.Stop();
            Console.WriteLine("intkey cost(tick): " + stopWatch3.ElapsedTicks.ToString());
            Console.WriteLine("intkey cost(ms): " + stopWatch3.ElapsedMilliseconds.ToString());
    

    输出: Tuplekey成本(勾号):104 tuplekey成本(ms):0 strkey成本(tick):12 strkey成本(ms):0 内部成本(勾号):66 Intkey成本(ms):0