1

I am trying to have a data structure with multiple string keys. To do this, I tried to create a Dictionary with string[] element. But the ContainsKey do no seem to work as I expect:

Dictionary<string[], int> aaa = new Dictionary<string[], int>();
int aaaCount = 0;
aaa.Add(new string[] { string1, string2 }, aaaCount++);

if (!aaa.ContainsKey(new string[] { string1, string2 }))
{
    aaa.Add(new string[] { string1, string2 }, aaaCount++);
}

I see that there are two entries in aaa after the execution of the code above while I was expecting only one. Is this the expected behaviour? How can I ensure that there are no duplicate entries in the Dictionary?

Note: I tried the same with a list as well (List and the result is the same - the Contains method does not really work with string[])

rizter
  • 25
  • 2
  • 9

4 Answers4

2

Because an array is a reference type, i.e., you are checking reference (identity) equality, not equality based on the values within the array. When you create a new array with the same values the arrays themselves are still two distinct objects, so ContainsKey returns false.

Using an array as a Dictionary key is a bit... odd. What are you trying to map here? There is probably a better way to do it.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
2

You need to create an IEqualityComparer<string[]> and pass it to the dictionary's constructor.

This tells the dictionary how to compare keys.
By default, it compares them by reference.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    [StructuralComparisons.StructuralEqualityComparer](http://msdn.microsoft.com/en-us/library/system.collections.structuralcomparisons.structuralequalitycomparer.aspx) should work. – Rick Sladkey Nov 29 '11 at 01:29
  • @RickSladkey: Actually, it won't, because it's weakly typed. – SLaks Nov 29 '11 at 01:34
2

If you want to use string[] as TKey, you should pass IEqualityComparer<string[]> to the constructor of Dictionary. Because Otherwise Dictionary uses standard comparison for TKey and in case of string[] it just compares references hence string[] is reference type. You have to implement IEqualityComparer yourself. It can be done in the following way:

(The implementation is quite naive, I provide it just as the starting point)

public class StringArrayComparer : IEqualityComparer<string[]>
{
    public bool Equals(string[] left, string[] right)
    {
        if (ReferenceEquals(left, right))
        {
            return true;
        }

        if ((left == null) || (right == null))
        {
            return false;
        }

        return left.SequenceEqual(right);
    }

    public int GetHashCode(string[] obj)
    {
        return obj.Aggregate(17, (res, item) => unchecked(res * 23 + item.GetHashCode()));
    }
}
Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
  • Don't use XOR; instead, use something like `res * 23 + item.GetHashCode()`. http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode – SLaks Nov 29 '11 at 01:38
  • Well, I've mentioned that the implementation is quite naive :) Anyway thanks for your comment! I've updated the answer so that it doesn't confuse anybody. – Dmitrii Lobanov Nov 29 '11 at 03:46
0

You may be better off, if your application supports it, to combine the string array into a single string.

We have numerous cases where two pieces of information uniquely identifies a record in a collection and in these cases, we join the two strings using a value that should never be in either string (i.e. Char(1)).

Since it is usually a class instance that is being added, we let the class specify the generation of the key so that the code adding to the collection only has to worry about checking a single property (i.e. CollectionKey).

competent_tech
  • 44,465
  • 11
  • 90
  • 113