Hashtable with MultiDimensional Key in C#
Solution 1
I think a better approach is to encapsulate the many fields of your multi-dimensional key into a class / struct. For example
struct Key {
public readonly int Dimension1;
public readonly bool Dimension2;
public Key(int p1, bool p2) {
Dimension1 = p1;
Dimension2 = p2;
}
// Equals and GetHashCode ommitted
}
Now you can create and use a normal HashTable and use this wrapper as a Key.
Solution 2
You can do this in C# 7.0 now with the new tuples:
// Declare
var test = new Dictionary<(int, bool), int>();
// Add
test.Add((1, false), 5);
// Get
int a = test[(1, false)];
Solution 3
I think this might be closer to what you're looking for...
var data = new Dictionary<int, Dictionary<bool, int>>();
Solution 4
Just in case anyone is here recently, an example of how to do this the quick and dirty way in .Net 4.0, as described by one of the commenters.
class Program
{
static void Main(string[] args)
{
var twoDic = new Dictionary<Tuple<int, bool>, String>();
twoDic.Add(new Tuple<int, bool>(3, true), "3 and true." );
twoDic.Add(new Tuple<int, bool>(4, true), "4 and true." );
twoDic.Add(new Tuple<int, bool>(3, false), "3 and false.");
// Will throw exception. Item with the same key already exists.
// twoDic.Add(new Tuple<int, bool>(3, true), "3 and true." );
Console.WriteLine(twoDic[new Tuple<int, bool>(3,false)]);
Console.WriteLine(twoDic[new Tuple<int, bool>(4,true)]);
// Outputs "3 and false." and "4 and true."
}
}
Solution 5
You need a key class for the Dictonary
that implements GetHashCode
correctly.
And you can extend Dictonary
to let you access it in a friendly way.
The KeyPair
class:
public class KeyPair<Tkey1, Tkey2>
{
public KeyPair(Tkey1 key1, Tkey2 key2)
{
Key1 = key1;
Key2 = key2;
}
public Tkey1 Key1 { get; set; }
public Tkey2 Key2 { get; set; }
public override int GetHashCode()
{
return Key1.GetHashCode() ^ Key2.GetHashCode();
}
public override bool Equals(object obj)
{
KeyPair<Tkey1, Tkey2> o = obj as KeyPair<Tkey1, Tkey2>;
if (o == null)
return false;
else
return Key1.Equals(o.Key1) && Key2.Equals(o.Key2);
}
}
Extend Dictonary<>
:
public class KeyPairDictonary<Tkey1, Tkey2, Tvalue>
: Dictionary<KeyPair<Tkey1, Tkey2>, Tvalue>
{
public Tvalue this[Tkey1 key1, Tkey2 key2]
{
get
{
return this[new KeyPair<Tkey1, Tkey2>(key1, key2)];
}
set
{
this[new KeyPair<Tkey1, Tkey2>(key1, key2)] = value;
}
}
}
You can use it like this:
KeyPairDictonary<int, bool, string> dict =
new KeyPairDictonary<int, bool, string>();
dict[1, false] = "test";
string test = dict[1, false];
Scott Schulthess
Updated on July 05, 2022Comments
-
Scott Schulthess almost 2 years
I'm basically looking for a way to access a hashtable value using a two-dimensional typed key in c#.
Eventually I would be able to do something like this
HashTable[1][false] = 5; int a = HashTable[1][false]; //a = 5
This is what I've been trying...hasn't worked
Hashtable test = new Hashtable(); test.Add(new Dictionary<int, bool>() { { 1, true } }, 555); Dictionary<int, bool> temp = new Dictionary<int, bool>() {{1, true}}; string testz = test[temp].ToString();
-
David M about 15 yearsDon't forget you need to override GetHashCode and Equals to use this in a Hashtable.
-
JaredPar about 15 years@David, not in this case. The default implementation of Equals will just perform equality on all of the fields which will work in this case. GetHashcode may not be as effiecient as the user would like but it will also function with the default implementation.
-
JaredPar about 15 years@David, that being said, it's usually good practice to actually do so.
-
Paul Ruane about 15 yearsThe two parts of the key. In the original post, this was the integer 1 and the boolean false, hence I have concatenated these two in the sample code. (The delimiter is not strictly necessary in this example.)
-
Adam Houldsworth over 11 years@JaredPar Doing so would be worthwhile. We recently uncovered a performance issue with the default implementation of
GetHashCode
forstruct
types. Manual implementation completely removed this bottleneck. Also, though a different solution, we find that the "dictionary within a dictionary" approach operates faster on all common actions (Dictionary<int, Dictionary<string, object>>
). However, this doesn't allow for null portions of the composite key, whereas astruct
/class
key as above could easily allow for nulls without any extra legwork. -
bacar almost 11 yearsThis works, but as @Adam mentions it is worth bearing in mind that the performance of struct's default
GetHashCode
might be terrible (or it might be just fine). Either way it will be correct (i.e. consistent withEquals()
). Some structs, such asKeyValuePair<ushort, uint>
, appear to use no fields from the struct at all and always return the same hash value - such keys would give you O(n) Dictionary lookup. Just be aware that struct isn't a panacea and be aware that you may eventually have to implement your own hashcode. -
JaredPar almost 11 years@bacar i didn't feel your comments were unhelpful at all. It is important to understand that this will create hash codes in a predetermined way that may very well be terrible for your application. If profiling showed this was type was causing a lot of bucket collisions that would absolutely be the correct place to go
-
Eamon Nerbonne almost 10 yearsBecause the default implementations of
GetHashCode
&Equals
are so slow and prone to hash-collisions, I've implemented ValueUtils which uses runtime code generation to efficiently implement those for you - that makes this approach a lot more palatable (in particular it addresses @bacar's problems). -
pogosama almost 6 yearsNote: To use this on an older .NET Target Framework, an additional nuget package is needed.
-
Parrhesia Joe over 5 yearsThis has some advantages. It is clearer in use
citiesByState["wa"]["seattle"]
is beautiful code. Note, though, it has to do two hash compares and isEquals per lookup. A single dictionary with a composite (tuple) key may be significantly faster, when performance is critical. I generally prefer the form in this answer because of the elegance, but 7.0 makes tuples a lot less ugly. -
Keith Stein almost 4 yearsNote: This also works in VB.NET as of Visual Basic 2017 (15.x)