BlackWasp
.NET Framework
.NET 1.1

The Hashtable Collection

The thirty-eighth part of the C# Fundamentals tutorial describes the use of the Hashtable class.  This provides general-purpose dictionary collections allowing the items in a collection to be addressed not by an index number, but by an object-based key.

What is a Hash Table?

In computing, a hash table is a data structure used to store a set of values that can each be identified by a unique key.  The key can then be used as a lookup to find any individual item in the set.  However, rather than storing the unique key value directly, the key is hashed.  The hashing process converts the key data into an index number and the value data linked to the key is placed in a storage location defined by that index.

The main advantage of the hash table structure is the ability to rapidly locate items, even in very large sets of data.  If the key value of a desired data value is known, the key can be hashed and the value's location found directly.  There is no requirement to iterate through the entire data set to find an item or to sort the set in order to perform a binary search.

The Hashtable Collection

The .NET framework provides the Hashtable class, which provides all of the functionality required to implement a hash table with no additional development.  The Hashtable is a general-purpose dictionary collection.  Each item within the collection is a DictionaryEntry object with two properties: a key object and a value object.  These are known as key / value pairs.

When items are added into a dictionary, a hash code is generated automatically.  This code is hidden from the developer with all direct access to individual values being achieved using the key object for identification.  As the items in the collection are ordered according to the hidden hash code, the items should be considered to be randomly ordered.

Implemented Collection Interfaces

The Hashtable collection implements the properties and methods of the IDictionary and ICollection interfaces.  This behaviour is described in the earlier Collection Interfaces article.  The rest of this article describes the additional functionality provided by Hashtable.

Declaring a Hashtable

Constructors

In the .NET framework version 1,1, the Hashtable class includes ten different constructors.  Later releases of the framework include even more constructors.  In this beginners' article, we will consider four of the basic constructors.  The simplest of these requires no parameters.  The following code creates a new, empty hash table.  The Hashtable class is found in the System.Collections namespace so to execute the examples, add using System.Collections; to the source code.

Hashtable myData = new Hashtable();

When a Hashtable is created, it has a limited capacity.  When the limit is reached, the capacity is automatically increased to allow further storage.  The storage requirements of a hash table structure are less predictable that those of other collections as the exact usage is determined by the hash values generated from item keys.  To allow for this, when the size of a Hashtable is increased, the new capacity is set to the smallest prime number that is at least double the existing capacity.  For example, if the initial capacity is 5, this is doubled to 10 and the next prime number, 11, is used as the new capacity.

The nature of a Hashtable dictionary means that the capacity is always considered to be approximate.  It is possible to set the initial approximate capacity by passing it as an integer parameter to the constructor.  This is useful when the maximum size of the collection is known as it removes the need for the Hashtable to be resized.  This improves the performance of the collection.

Hashtable myData = new Hashtable(100);

All hash tables have a load factor.  The load factor is the ratio between the number of items in the collection and the number of buckets, or storage spaces available.  The load factor for a Hashtable dictionary defaults to 1.0 to give a good balance between performance and memory requirements.  The value may be altered in the constructor by specifying an initial capacity as an integer and a second, decimal parameter for the load factor.  Valid values for the load factor are between 0.1 and 1.0.  A value of 0.1 indicates that only one tenth of the storage space in the collection will be used before the Hashtable capacity is increased.  This would give a high performing but memory-hungry dictionary.

Hashtable myData = new Hashtable(100, 0.1);

The final constructor to be investigated allows a new Hashtable to be created and be fully populated using the contents of any other collection that supports the IDictionary interface.  Every item in the dictionary passed as the constructor's only parameter is copied into the new collection.

Hashtable myData = new Hashtable();

myData.Add("key1","value1");

Hashtable myCopy = new Hashtable(myData);   // Copies myData into myCopy

Modifying Hashtable Contents

Adding an Item

An item is added to a Hashtable in the same manner as for all collections that implement the IDictionary interface.  This has been described earlier in the C# Fundamentals tutorial in the Collection Interfaces article.  However there are two important restrictions that should be noted.  Firstly, the key of an item to be added must be an instantiated object or value; it cannot be null.  Attempting to add to a Hashtable where the item's key is null causes an ArgumentNullException.  An ArgumentException is thrown if the key is not unique.  These restrictions to not apply to the value part of a Hashtable entry.

Hashtable myData = new Hashtable();

myData.Add("key1","value1");
myData.Add("key1","value2");                // Duplicate: ArgumentException
myData.Add(null,"value2");                  // Null Key: ArgumentNullException

Updating Using the Key

The Hashtable can be modified directly using the key as if it were an array's index number.  The key is appended to the collection's name between square brackets.  If the key assigned to already exists, the value of the corresponding item is updated.  If the key does not exist, the key and the value are added to the Hashtable as a new item.

Hashtable fruit = new Hashtable();

fruit["apple"] = "Granny Smith";            // New item created
fruit["apple"] = "Golden Delicious";        // Item updated
fruit["banana"] = "Cavendish";
fruit["cherry"] = "Yamagata";

Reading Hashtable Information

One of the key benefits of the Hashtable collection is the facility to directly read an entry using its key as a reference.  This is also the simplest method to retrieve a single item from the dictionary.  As with arrays and index-based collections, the key is appended to the collection name in a pair of square brackets.

NB: The following examples use strings for keys and values to demonstrate the use of Hashtables.  Remember that when developing your own programs, both the key and value items can be any object type.

Hashtable fruit = new Hashtable();

fruit.Add("apple","Granny Smith");
fruit.Add("banana","Cavendish");
fruit.Add("cherry","Yamagata");
fruit.Add("orange","Seville");

string banana = fruit["banana"].ToString();
Console.WriteLine(banana);                  // Outputs "Cavendish"

Iterating Using DictionaryEntry Objects

When an item is added to a dictionary, the key and the value elements are combined into a single object of the type DictionaryEntry.  Using the foreach statement, it is possible to iterate through each item and extract its key and value from the DictionaryEntry object.  The following example outputs the details of each key / value pair to the console.  Note that the order of the items in the collection is not necessarily the same as the order in which they were originally added.

Hashtable fruit = new Hashtable();

fruit.Add("apple","Granny Smith");
fruit.Add("banana","Cavendish");
fruit.Add("cherry","Yamagata");
fruit.Add("orange","Seville");

foreach(DictionaryEntry de in fruit)
    Console.WriteLine("{0} \t: {1}", de.Key, de.Value);

/* OUTPUT

cherry  : Yamagata
apple   : Granny Smith
banana  : Cavendish
orange  : Seville

*/

Values

In most cases you will not want to iterate through the list of DictionaryEntry objects as you will only want to interrogate the value of each item.  The Values property returns an ICollection object that contains only the values for each item in the Hashtable.  This can be looped through directly.

Hashtable fruit = new Hashtable();

fruit.Add("apple","Granny Smith");
fruit.Add("banana","Cavendish");
fruit.Add("cherry","Yamagata");
fruit.Add("orange","Seville");

foreach(string value in fruit.Values)
    Console.WriteLine(value);

/* OUTPUT

Yamagata
Granny Smith
Cavendish
Seville

*/

Keys

The Keys property is similar to the Values property.  It returns an ICollection object containing all of the keys of the items in a Hashtable.

Hashtable fruit = new Hashtable();

fruit.Add("apple","Granny Smith");
fruit.Add("banana","Cavendish");
fruit.Add("cherry","Yamagata");
fruit.Add("orange","Seville");

foreach(string value in fruit.Keys)
    Console.WriteLine(value);

/* OUTPUT

cherry
apple
banana
orange

*/

ContainsKey

As described in a previous article within the C# Fundamentals tutorial, all collections that implement IDictionary include a method named Contains that returns a Boolean value indicating if a specified key exists within the collection.  The Hashtable includes this method and another named ContainsKey that provides exactly the same functionality.

ContainsValue

In addition to determining if a specific key exists within a Hashtable, it can be useful to check if a value occurs within the collection.  The ContainsValue operates in a similar manner to Contains and ContainsKey; the object to test for is passed as the only parameter.  The resultant Boolean is true if the object exists and false if not.

Hashtable fruit = new Hashtable();

fruit.Add("apple","Granny Smith");
fruit.Add("banana","Cavendish");
fruit.Add("cherry","Yamagata");
fruit.Add("orange","Seville");

Console.WriteLine(fruit.ContainsValue("Seville"));      // Outputs "True"
Console.WriteLine(fruit.ContainsValue("Valencia"));     // Outputs "False"

Creating a Thread-Safe Hashtable Wrapper

In the earlier article discussing collection interfaces, the concept of a thread-safe synchronised collection was described.  In order to create a thread-safe Hashtable, a synchronised wrapper dictionary is generated using the static Synchronized method.

Hashtable myDictionary = new Hashtable();

Hashtable myThreadSafe = Hashtable.Synchronized(myDictionary);

Console.WriteLine(myThreadSafe.IsSynchronized);	        // Outputs "True"
Link to this Page20 May 2007
RSS RSS Feed
93 users on-line