Updated on

Use Dictionary<TKey, TValue>. It is type-safe, it does not box value types, and it is measurably faster and lighter than Hashtable at every size we tested.

To download the source code for this article, you can visit our GitHub repository.

Hashtable is the non-generic original from .NET Framework 1.0, kept for backward compatibility. Microsoft says so in the Hashtable reference itself: “We don’t recommend that you use the Hashtable class for new development. Instead, we recommend that you use the generic Dictionary<TKey,TValue> class.” (Hashtable Class, Microsoft Learn). The only reasons to reach for it today are maintaining code that already uses it, or needing its one genuinely distinct behaviour: a missing key returns null instead of throwing.

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.

A detailed article on Dictionary is also available on the website, please go through it to learn more about it.

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

Hashtable predates generics by a decade; that is why it still shows up in older codebases and interview questions.

For a detailed look at Hashtable on its own — constructors, capacity, and thread-safety options — see our dedicated guide.

Please go through the detailed article on the Hashtable to learn more about it.

Is There a HashMap in C#?

No. C# has no type called HashMap, and the equivalent is Dictionary<TKey, TValue>.

The name comes from Java, where HashMap<K, V> is the standard hash-based key-value collection. Developers moving to C# reach for the same name, find nothing, and often land on Hashtable because it sounds closest. That is the wrong answer twice over, since Hashtable is neither the modern type nor the one that matches Java’s semantics.

Dictionary<TKey, TValue> is the closer match. Both are generic, both are hash-based, both give amortised O(1) lookup, and neither is thread-safe on its own.

Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!

The differences worth knowing when porting are around missing keys and nulls. Java’s HashMap returns null for an absent key and accepts a null key; the C# Dictionary throws KeyNotFoundException from its indexer and rejects a null key. So map.get(k) becomes dict.TryGetValue(k, out var v), not dict[k].

For a concurrent map, the C# equivalent is ConcurrentDictionary<TKey, TValue>.

Two related types come up often alongside this comparison: ConcurrentDictionary in C# for thread-safe access, and HashSet in C# for when only the keys matter, not the values.

Dictionary vs Hashtable in C#: What Is the Difference?

The difference that matters is generics. Dictionary<TKey, TValue> is typed at compile time, so the compiler rejects a wrong-typed key or value before the code runs, while Hashtable stores everything as object and finds the same mistake at runtime, as an InvalidCastException.

That single design choice produces the rest. Because Hashtable holds object, every int, DateTime, or struct we put in gets boxed onto the heap and unboxed on the way out, which costs both time and allocations. Dictionary stores value types directly and pays neither.

Enumeration differs too: iterating a Dictionary yields typed KeyValuePair<TKey, TValue> items, while a Hashtable yields DictionaryEntry with object members that need casting.

The one behavioural difference that is not about types is missing keys. Indexing a Hashtable with an absent key returns null; indexing a Dictionary throws KeyNotFoundException, which is why TryGetValue() exists.

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.

See detecting whether a dictionary key exists for the full set of lookup patterns.

Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!

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 |    18.79 ms |  0.367 ms |  0.680 ms |   11.88 MB |
|       BenchmarkHashtable |    29.33 ms |  0.867 ms |  2.373 ms |    15.3 MB |

For 100,000 elements, the BenchmarkDictionary method takes around 18.79 ms, while the BenchmarkHashTable method takes around 29.33 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 | 1,499.32 ms | 29.883 ms | 37.792 ms | 1086.82 MB |
|  BenchmarkLargeHashtable | 2,779.55 ms | 27.278 ms | 25.516 ms | 2003.88 MB |

For even a relatively larger input, Dictionary performs better than Hashtable. The mean time taken by Dictionary is around 1.50 seconds in comparison to the 2.78 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.

Should We Use a Dictionary or a Hashtable?

We use Dictionary<TKey, TValue> unless we are maintaining code that already uses Hashtable.

The case is not close. Type safety catches bugs at compile time, no boxing means fewer allocations, and the benchmark in the previous section shows Dictionary finishing faster while allocating roughly half the memory.

Hashtable is worth keeping in exactly two situations. The first is existing code, where swapping the type ripples through every cast and every DictionaryEntry loop for no functional gain. The second is the null-on-missing-key behaviour, if some code genuinely depends on it, though TryGetValue() expresses the same intent more clearly and without a lookup that silently succeeds.

Two neighbours are worth knowing. For concurrent access, neither type is safe; use ConcurrentDictionary<TKey, TValue> rather than locking a Dictionary or calling Hashtable.Synchronized(). For keys without values, HashSet<T> is the right shape.

CriterionDictionary<TKey, TValue>Hashtable
NamespaceSystem.Collections.GenericSystem.Collections
Type safetyCompile-time: keys and values typedNone, both are object
Boxing of value typesNoYes, on every add and read
Enumerated itemKeyValuePair<TKey, TValue>DictionaryEntry
Missing key via indexerThrows KeyNotFoundExceptionReturns null
Safe lookupTryGetValue()ContainsKey() then index
Thread safetyNone, use ConcurrentDictionarySynchronized wrapper via Hashtable.Synchronized()
OrderingNot guaranteedNot guaranteed
Available since.NET Framework 2.0.NET Framework 1.0
Use it whenAlways, by defaultMaintaining existing code

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.

For where Dictionary and Hashtable fit among C#’s other collection types, see an overview of .NET collections.

Tested with .NET 10.0.10.