Introduction
Although you can't deny the advantages of Hashtable, sometime it is not enough. Sometime you need to have a better duality - accessing the object from it's key, and the key from the object. This becomes doubly useful and important since the basic Hashtable allows you to use almost any object as the key.
A good example of this need is when displaying complex objects in a TreeView. The ability to retrieve the complex object from its matching TreeNode and vice-versa is very handy. For once, you can easily getthe selected item, or jump to a certain node.
Implementation
The implementation of such a BiDirectionalHashtable is quite simple. All one has to do is keep two simple Hashtables - one where the keys & objects play their normal part. However, in the other Hashtable, the roles are swapped - the objects are used as the keys, and the keys are kept in the Hashtable as the values.
public void AddItem(object key, object item)
{
if (keysToItems.ContainsKey(key) || itemsToKeys.ContainsKey(item))
{
keysToItems.Remove(key);
itemsToKeys.Remove(item);
}
keysToItems[key] = item;
itemsToKeys[item] = key;
}
private Hashtable keysToItems = new Hashtable();
private Hashtable itemsToKeys = new Hashtable();
Once this is acomplished, all you are left to do is provide methods to easily handle such a class. Methods such as GetKey()
and GetItem()
are of course called for, as the respective Remove
methods.
public object GetKey(object item)
{
if (item == null)
throw new ArgumentNullException("keitemy");
return itemsToKeys[item];
}
public object GetItem(object key)
{
if (key == null)
throw new ArgumentNullException("key");
return keysToItems[key];
}
public void RemoveByKey(object key)
{
if (key == null)
throw new ArgumentNullException("key");
object item = keysToItems[key];
keysToItems.Remove(key);
itemsToKeys.Remove(item);
}
public void RemoveByItem(object item)
{
if (item == null)
throw new ArgumentNullException("item");
object key = itemsToKeys[item];
keysToItems.Remove(key);
itemsToKeys.Remove(item);
}
You can also supply an indexer, but I woul recommend one only if you are implementing a strong-typed BiDirectionalHashtable, because having a weak-typed index will leave the decision whether the index is a key or an object to the class. This can work only if you can assur that the same object cannot be used as both a key and as a value.
Summary
Although this is a very basic issue, its uses are many, and it certainly ease the life of a programmer, and make a clear and easier to maintain code. OF course, the use of Generics in VS2005 make everything even simplier and cleaner.
History
4/11/2005 - Following tonyt and leppie remarks, addition of items now checks existance of previous items and remove them.