views:

1773

answers:

3

I am trying to make a Dictionary lookup table in C#. I need to resolve a 3-tuple of values to one string. I tried using arrays as keys, but that did not work, and I don't know what else to do. At this point I am considering making a Dictionary of Dictionaries of Dictionaries, but that would probably not be very pretty to look at, though it is how I would do it in javascript.

+2  A: 

I would overload your Tuple with a proper GetHashCode, and just use it as the key.

As long as you overload the proper methods, you should see decent performance.

John Gietzen
IComparable doesn't have an effect on how keys are stored or located in a Dictionary<TKey,TValue>. It's all done through GetHashCode() and an IEqualityComparer<T>. Implementing IEquatable<T> will achieve better performance because it alleviates the boxing caused by the default EqualityComparer, which falls back on the Equals(object) function.
Dustin Campbell
I was going to mention GetHashCode, but I thought that the Dictionary used IComparable in the case that the HashCodes were Identical... guess I was wrong.
John Gietzen
+17  A: 

You define a Tuple and use that as the key. The Tuple needs to override GetHashCode, Equals and IEquatable:

struct Tuple<T, U, W> : IEquatable<Tuple<T,U,W>>
{
    readonly T first;
    readonly U second;
    readonly W third;

    public Tuple(T first, U second, W third)
    {
        this.first = first;
        this.second = second;
        this.third = third;
    }

    public T First { get { return first; } }
    public U Second { get { return second; } }
    public W Third { get { return third; } }

    public override int GetHashCode()
    {
        return first.GetHashCode() ^ second.GetHashCode() ^ third.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }
        return Equals((Tuple<T, U, W>)obj);
    }

    public bool Equals(Tuple<T, U, W> other)
    {
        return other.first.Equals(first) && other.second.Equals(second) && other.third.Equals(third);
    }
}

The rumor is that such a type will be included in .NET 4.0. It already is in the .NET 4.0 Beta, so it is quite likely.

Hallgrim
Download a copy of VS2010 Beta, and you can personally confirm that such a type in actual fact is in the .NET 4.0 libraries ;)
jerryjvl
This struct should also implement IEquatable<Tuple<T,U,W>>. That way you can avoid boxing when Equals() is called in the case of hash code collisions.
Dustin Campbell
Thanks Dustin. Added it to the example.
Hallgrim
Is it possible/better to use "as" operator instead of GetType() and a cast in the Equals?
Yacoder
@Yacoder: With "as" you also get all the types that derives from Tuple (e.g. class ExtendedTuple : Tuple).
Hallgrim
You *almost* got it right, no ToString implementation. Though that's mostly icing on the cake you implemented the important stuff. +1
RCIX
+1  A: 

If for some reason you really want to avoid creating your own Tuple class, or using on built into .NET 4.0, there is one other approach possible; you can combine the three key values together into a single value.

For example, if the three values are integer types together not taking more than 64 bits, you could combine them into a ulong.

Worst-case you can always use a string, as long as you make sure the three components in it are delimited with some character or sequence that does not occur inside the components of the key, for example, with three numbers you could try:

string.Format("{0}#{1}#{2}", key1, key2, key3)

There is obviously some composition overhead in this approach, but depending on what you are using it for this may be trivial enough not to care about it.

jerryjvl
While this *would* work, it has a bad code smell.
John Gietzen
I'd say that it depends strongly on context though; if I had three integer types to combine, and performance was not critical, this works perfectly fine with minimal chance of making a mistake. Of course, all of this is completely redundant as of .NET 4, since Microsoft will be providing us with (presumably correct!) Tuple types out of the box.
jerryjvl
Nice idea for large domains.
ymihere