字典键的两个字符串

时间:2011-02-20 20:26:53

标签: c# string data-structures dictionary hashcode

我有两个字符串,我想将它们用作字典键,但我有点懒于创建另一个对象,计算字符串的哈希码等。

所以不是这样,我可以获得两个字符串的哈希码,添加它们并将结果用作Dictionary的键吗?

有可能造成碰撞吗?右?

有什么想法吗?

4 个答案:

答案 0 :(得分:19)

  

我有两根弦,我想   使用它们作为字典键,但我   有点懒,不能创建另一个对象

在.NET 4.0中,您可以使用Tuple<T1, T2>类作为键,T1和T2 = string。

  

我可以获得两个字符串的哈希码   ,添加它们并使用结果作为   字典的关键?

用于组合哈希码的公式Tuple<T1, T2>类似于(未记录或保证不会更改):((h1 << 5) + h1) ^ h2,它应该足以满足您的需要。顺便说一句,天真添加通常不是组合哈希码的最佳方式。

  

有可能造成碰撞吗?   右?

即使使用单个字符串,也始终可以使用此功能。字符串多于32位整数。

答案 1 :(得分:10)

如果您使用的是.NET 4,则可以使用Tuple类:

Dictionary<Tuple<string, string>, TValue> dict = new ...

如果你不在.NET 4上,你应该创建自己的类型来保存它。

您可以使用KeyValuePair结构,但它从基值类型继承相关方法,因此在很大程度上依赖于反射。这有性能影响(见答案的底部。)

对于KeyValuePair:

Dictionary<KeyValuePair<string, string>, TValue> dict = new ...

如果您不想自己做饭,可以使用以下类型:

public struct SimpleTuple<TValue1, TValue2>
{
    private readonly TValue1 _Value1;
    private readonly TValue2 _Value2;

    public SimpleTuple(TValue1 value1, TValue2 value2)
    {
        _Value1 = value1;
        _Value2 = value2;
    }

    public TValue1 Value1 { get { return _Value1; } }
    public TValue2 Value2 { get { return _Value2; } }

    public int GetHashCode()
    {
        unchecked
        {
            int result = 37;

            result *= 23;
            if Value1 != null)
                result += Value1.GetHashCode();

            result *= 23;
            if (Value2 != null)
                result += Value2.GetHashCode();

            return result;
        }
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj.GetType() != typeof(SimpleTuple<TValue1, TValue2>))
            return false;

        var other = (SimpleTuple<TValue1, TValue2>)obj;
        return Equals(other.Value1, Value1) && Equals(other.Value2, Value2);
    }
}

当然,KeyValuePair也适用于.NET 4.0,就像 good 一样糟糕。

至于碰撞,这取决于你的意思。哈希表(字典在内部使用哈希表结构)总是有可能获得关键冲突,但这就是比较发挥作用的地方。如果两个不同的密钥生成相同的哈希代码,则字典类将比较密钥与密钥,以查看它们是否真的是相同的值,或者只生成相同的哈希代码。

最好用pidgeonhole principle (Wikipedia)描述哈希表总是有可能发生碰撞的原因。

这意味着如果你有两个不同的键会导致碰撞,那就不会有问题,它们都会以正确的值存储在字典中。

当然,如果您创建两次相同的密钥,字典会将其计为相同的密钥,并且无法添加新值,或覆盖现有密钥(取决于您要求它添加值的方式。)

这将在重复键上抛出异常:

dict.Add(key, value);

这将添加或覆盖现有的:

dict[key] = value;

为了回应Ani的评论,我为LINQPad编写了以下简单的测试脚本。输出是:

KeyValuePair: 975ms
MyKeyValuePair: 52ms

脚本:

void Main()
{
    const int iterations = 10 * 1000 * 1000;

    // JIT preheat
    Test1(1);
    Test2(1);

    Stopwatch sw = Stopwatch.StartNew();
    Test1(iterations);
    sw.Stop();
    Debug.WriteLine("KeyValuePair: " + sw.ElapsedMilliseconds + "ms");

    sw = Stopwatch.StartNew();
    Test2(iterations);
    sw.Stop();
    Debug.WriteLine("MyKeyValuePair: " + sw.ElapsedMilliseconds + "ms");
}

public static void Test1(int iterations)
{
    for (int index = 0; index < iterations; index++)
    {
        var kvp = new KeyValuePair<int, int>(index, index);
        kvp.GetHashCode();
    }
}

public static void Test2(int iterations)
{
    for (int index = 0; index < iterations; index++)
    {
        var kvp = new MyKeyValuePair<int, int>(index, index);
        kvp.GetHashCode();
    }
}

public struct MyKeyValuePair<TKey, TValue>
{
    private readonly TKey _Key;
    private readonly TValue _Value;

    public MyKeyValuePair(TKey key, TValue value)
    {
        _Key = key;
        _Value = value;
    }

    public TKey Key { get { return _Key; } }
    public TValue Value { get { return _Value; } }

    public int GetHashCode()
    {
        unchecked
        {
            int result = 37;

            result *= 23;
            if (Key != null)
                result += Key.GetHashCode();

            result *= 23;
            if (Value != null)
                result += Value.GetHashCode();

            return result;
        }
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj.GetType() != typeof(MyKeyValuePair<TKey, TValue>))
            return false;

        var other = (MyKeyValuePair<TKey, TValue>)obj;
        return Equals(other.Key, Key) && Equals(other.Value, Value);
    }
}

答案 2 :(得分:3)

使用元组:

var dict = new Dictionary<Tuple<string,string>,SomeType>();
dict.Add(Tuple.Create("Hello","World"), new SomeType());

答案 3 :(得分:3)

简单的解决方案,以及适用于所有.net版本的解决方案。只需将字符串连接在一起即可。

var dictionary = new Dictionary<string, int>();
dictionary.Add("The meaning" + " of life, the universe, and everything", 42);

当然这仅适用于2个字符串(尽管你可以在许多其他类型上使用.ToString())并且如果你不需要仅通过两个字符串中的一个来查找字典,但是如果你同时使用它们非常简单。