Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Khác biệt giữa Hashtable và Dictionary trong C#

Tóm tắt nhanh

Đặc điểmHashtableDictionary<TKey, TValue>
GenericsNon-generic (object key/value)Generic (type-safe)
NamespaceSystem.CollectionsSystem.Collections.Generic
Type SafetyKhông (cast cần thiết)Có (compile-time)
Boxing/UnboxingCó (value types)Không
PerformanceChậm hơn (boxing + cast)Nhanh hơn
Null KeyCho phépKhông cho phép (throw ArgumentNullException)
Null ValueCho phépCho phép (reference types)
Missing Key BehaviorTrả về nullThrow KeyNotFoundException
Enumeration OrderKhông đảm bảoInsertion order (từ .NET Core 3.0+)
Thread SafetyHashtable.Synchronized() wrapperKhông thread-safe (dùng ConcurrentDictionary)

Ví dụ Hashtable (Legacy)


using System.Collections;

var ht = new Hashtable();
ht.Add(1, "One");           // int → object (boxing)
ht.Add("Key", 100);         // string key, int value
ht.Add(DateTime.Now, "Time"); // object key

// Truy xuất - CẦN CAST
string s = (string)ht[1];           // Unboxing + cast
int i = (int)ht["Key"];             // Unboxing + cast

// Key không tồn tại
var missing = ht[999]; // null - không throw exception

// Enumeration
foreach (DictionaryEntry entry in ht) 
{
    Console.WriteLine($"{entry.Key} ({entry.Key.GetType()}): {entry.Value}");
}

Ví dụ Dictionary (Modern)


using System.Collections.Generic;

var dict = new Dictionary();
dict.Add(1, "One");
// dict.Add("Key", 100); // ❌ Compile error: type mismatch

// Truy xuất - TYPE SAFE
string s = dict[1]; // string trực tiếp

// Key không tồn tại
// var missing = dict[999]; // ❌ Throw KeyNotFoundException

// Safe access
if (dict.TryGetValue(999, out var value)) 
{
    Console.WriteLine(value);
}
else 
{
    Console.WriteLine("Not found");
}

// Hoặc C# 7+ 
dict.TryGetValue(999, out string value); // value = null nếu không tìm thấy

// Enumeration - giữ insertion order (.NET Core 3.0+)
foreach (var kvp in dict) 
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

Boxing/Unboxing Cost Demo


using System.Diagnostics;

const int N = 1_000_000;

// Hashtable - boxing mỗi Add, unboxing mỗi Get
var ht = new Hashtable();
var sw = Stopwatch.StartNew();
for (int i = 0; i < N; i++) ht.Add(i, i);
for (int i = 0; i < N; i++) _ = (int)ht[i];
Console.WriteLine($"Hashtable: {sw.ElapsedMilliseconds} ms");

// Dictionary - no boxing
var dict = new Dictionary();
sw.Restart();
for (int i = 0; i < N; i++) dict.Add(i, i);
for (int i = 0; i < N; i++) _ = dict[i];
Console.WriteLine($"Dictionary: {sw.ElapsedMilliseconds} ms");

// Kết quả điển hình: Hashtable ~200ms, Dictionary ~50ms (4x nhanh hơn)

Thread Safety


// Hashtable - có wrapper synchronized (legacy)
var syncHt = Hashtable.Synchronized(new Hashtable());
// Chỉ các method đơn lẻ thread-safe, compound operations KHÔNG

// Dictionary - KHÔNG thread-safe
// Multi-thread read OK, write cần lock
lock (dict) { dict[key] = value; }

// Modern: ConcurrentDictionary (lock-free, high perf)
using System.Collections.Concurrent;
var cd = new ConcurrentDictionary();
cd.TryAdd(1, "One");
cd.AddOrUpdate(1, "New", (k, v) => v + " Updated");
cd.GetOrAdd(2, k => "Computed");

Khi nào dùng Hashtable?

  • Legacy code (.NET Framework 1.x/2.x).
  • COM Interop, old APIs yêu cầu IDictionary non-generic.
  • Cần null key (hiếm).
  • Deserialize dữ liệu cũ không có type info.

Khi nào dùng Dictionary?

  • Mọi code mới (.NET 2.0+).
  • Cần type safety, performance.
  • Generic collections standard.

Dictionary Alternatives

TypeUse Case
ConcurrentDictionaryMulti-thread read/write
SortedDictionarySorted by key, O(log n)
SortedListSorted, index access, small data
ImmutableDictionaryFunctional, thread-safe, persistent
FrozenDictionaryRead-heavy, ultra-fast lookup (.NET 8+)
ConditionalWeakTableAttach data to objects (weak keys)

Migration Hashtable → Dictionary


// Hashtable
var ht = new Hashtable { { "a", 1 }, { "b", 2 } };

// Convert to Dictionary
var dict = ht.Cast()
    .ToDictionary(de => (string)de.Key, de => (int)de.Value);

// Or manual
var dict2 = new Dictionary();
foreach (DictionaryEntry entry in ht) 
{
    dict2[(string)entry.Key] = (int)entry.Value;
}