In this article, we are going to compare Dictionary and Hashtable in C#. We will start with a brief introduction to both data structures and then proceed to the comparison.
Let’s begin.
What Is a Dictionary?
Dictionary<TKey,TValue>
represents a generic collection of keys and values. It resides in the Systems.Collections.Generic
namespace.
Let’s create an Order
class first, and add two simple properties OrderId
and Quantity
:
public class Order { public Guid OrderId { get; set; } public decimal Quantity { get; set; } }
Now, let’s use the Order
class to create a dictionary with an integer
key and Order
value and populate it with test data:
var ordersDictionary = new Dictionary<int, Order>(); ordersDictionary.Add(1, new Order { OrderId = Guid.NewGuid(), Quantity = 25 }); ordersDictionary.Add(2, new Order { OrderId = Guid.NewGuid(), Quantity = 35 }); ordersDictionary.Add(3, new Order { OrderId = Guid.NewGuid(), Quantity = 45 });
We can not deviate from the type of Dictionary declared, i.e. we will get a compilation error if we add a string
key instead of an integer
key in the ordersDictionary
. The same goes for the type of value as well, we can only add instances of Order
object.
Let’s loop through the Dictionary
:
foreach (KeyValuePair<int, Order> order in ordersDictionary) { Console.WriteLine($"Key: {order.Key}, Order Id: {order.Value.OrderId}, Quantity: {order.Value.Quantity}"); }
And inspect the result in the console:
Key: 1, Order Id: 768819fb-f589-4eae-b571-0fb0d18b6b67, Quantity: 25 Key: 2, Order Id: 81f111cb-8b65-4f7d-8e63-e42dab8b0454, Quantity: 35 Key: 3, Order Id: d333487e-3d35-4659-9599-5b4763564e14, Quantity: 45
A very important fact to know is that Dictionary
implements a Hashtable
internally.
What Is a Hashtable?
Hashtable
represents a collection of key/value pairs that are organized based on the hash code of the key. It resides in the Systems.Collections
namespace.
Key and Value both are of object
types in Hashtable
.
Let’s create a Hashtable
and populate it with test data:
var hashTable = new Hashtable(); hashTable.Add(1, 1009); hashTable.Add(2, "Chicago"); hashTable.Add(3, true); hashTable.Add("Country", "India");
We can add any type of key/value pair to a Hashtable
, it will not raise any compilation error.
Let’s loop through the Hashtable
:
foreach (DictionaryEntry item in hashTable.Keys) { Console.WriteLine($"Key: {item.Key}, Value: {item.Value}"); }
And inspect the result in the console:
Key: Country, Value: India Key: 3, Value: True Key: 2, Value: Chicago Key: 1, Value: 1009
Dictionary vs Hashtable
Now, we can compare a Dictionary
with a Hashtable
with respect to certain pointers.
Type Safety
Dictionary
is a type-safe collection. We encounter a compilation error if we add a random key or value other than what types are declared in a dictionary.
Hashtables
are not type-safe, we can add any type of key or value.
Enumerated Item
The enumerated item in the case of Dictionary
is KeyValuePair
, whereas it is DictionaryEntry
in the case of a Hashtable
.
However, Microsoft suggests using KeyValuePair
instead of DictionaryEntry
.
Boxing/Unboxing
Hashtable
uses object
type to hold things internally, hence it needs to do boxing/unboxing in the case of value types.
Dictionary
does not need to perform boxing/unboxing
because it is a type-safe collection.
Behavior in the Case of a Non-existent Key
Dictionary
throws the KeyNotFoundException
if we create a query with a key that does not exist, whereas a Hashtable
throws null
.
Let’s see this in action:
var countriesAndCapitals = new Dictionary<string, string> { { "India","New Delhi"}, { "Australia","Canberra"}, { "USA","Washington DC"}, { "UK","London"} }; var capitalOfFrance = countriesAndCapitals["France"]; Console.WriteLine(capitalOfFrance);
France does not exist as a key in the countriesAndCapitals
dictionary, hence the KeyNotFoundException
exception gets thrown:
Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'France' was not present in the dictionary.
The best way to prevent KeyNotFoundException
is to use Dictionary.TryGetValue
.
Dictionary vs Hashtable Performance
In this section, we will be using BenchmarkDotNet to measure the performance of a Dictionary
and Hashtable
.
Let’s see it in action:
private const int ONE_HUNDRED_THOUSAND = 100000; public static Dictionary<int, string> CreateSmallNumbersDictionary() { var dictionary = new Dictionary<int, string>(); for (var i = 0; i < ONE_HUNDRED_THOUSAND; i++) { dictionary.Add(i, $"Data{i}"); } return dictionary; } public static Hashtable CreateSmallNumbersHashTable() { var hashTable = new Hashtable(); for (var i = 0; i < ONE_HUNDRED_THOUSAND; i++) { hashTable.Add(i, $"Data{i}"); } return hashTable; }
We have 2 methods, namely CreateSmallNumbersDictionary
and CreateSmallNumbersHashTable
that returns Dictionary
and Hashtable
objects respectively. Both the Dictionary
and Hashtable
contain one hundred thousand (100,000) elements.
Now, we need to create the actual methods whose performance we would measure and decorate with [Benchmark]
attribute:
[MemoryDiagnoser] public class BenchmarkProcess { [Benchmark] public void BenchmarkDictionary() { Utility.ReadDictionary(Utility.CreateSmallNumbersDictionary()); } [Benchmark] public void BenchmarkHashtable() { Utility.ReadHashTable(Utility.CreateSmallNumbersHashTable()); } }
After we run the project in Release
mode, we would see the benchmark results in the BenchmarkDotNet.Artifacts
folder:
| Method | Mean | Error | StdDev | Allocated | |------------------------- |------------:|----------:|----------:|-----------:| | BenchmarkDictionary | 16.67 ms | 0.329 ms | 0.338 ms | 11.88 MB | | BenchmarkHashtable | 23.42 ms | 0.417 ms | 0.557 ms | 15.3 MB |
For 100,000 elements, the BenchmarkDictionary
method takes around 16.67 ms, while the BenchmarkHashTable
method takes around 23.42 ms. The dictionary method takes less memory as well.
Let’s take another test, this time we are going to go with 10,000,000 elements:
| Method | Mean | Error | StdDev | Allocated | |------------------------- |------------:|----------:|----------:|-----------:| | BenchmarkLargeDictionary | 2,043.07 ms | 36.277 ms | 32.159 ms | 1086.83 MB | | BenchmarkLargeHashtable | 3,113.23 ms | 60.324 ms | 56.427 ms | 2003.88 MB |
For even a relatively larger input, Dictionary
performs better than Hashtable
. The mean time taken by Dictionary
is around 2.04 seconds in comparison to the 3.11 seconds taken by Hashtable
.
The Dictionary
method consumes around 1087 MBs
of memory as compared to the 2004 MBs
of memory consumed by the Hashtable
method. A performance betterment of around 45%
.
Results Summary
Going by the test numbers, we can safely say that a Dictionary
performs better as compared to a Hashtable
for larger inputs. The performance will be actually comparable if we take into account smaller inputs.
We should choose Dictionary
over Hashtable
wherever required because Dictionary
is a generic collection and provides type safety. It does not go through the unnecessary process of boxing/unboxing in the case of value types.
Conclusion
In this article, we have learned about two of the common collections in C# – Dictionary and Hashtable, and we’ve seen what the differences between them are. We’ve also learned that in some cases we should prefer a Dictionary over a Hashtable.