bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] System.Collections.Hashtable — 非ジェネリックなハッシュテーブルの仕組みと使いどころ

[C#] System.Collections.Hashtable — 非ジェネリックなハッシュテーブルの仕組みと使いどころ

May 17, 2026 C# , .NET bucket-sort

System.Collections.Hashtable は、.NET Framework 1.0 から提供されている 非ジェネリックなキー・値ストア(連想配列) です。Dictionary<TKey, TValue>(.NET 2.0+)の登場で主役の座を譲りましたが、いまだに古い API のシグネチャや構成ファイル系のクラスで顔を出します。本記事では Hashtable の 内部構造・インターフェース・性能特性・使いどころ を整理します。

Hashtable とは

  • 名前空間:System.Collections
  • 内部構造:オープンアドレス法(ダブルハッシュ) によるハッシュテーブル
  • キー・値はともに object。値型はボックス化される
  • 既定のロードファクター(loadFactor)は 1.0、要素数が容量を超えたら 再ハッシュ で約 2 倍に拡張
  • null キーは不可(ArgumentNullException)。null 値は可
using System.Collections;

var table = new Hashtable
{
    ["apple"]  = 150,
    ["orange"] = 120,
};

if (table.ContainsKey("apple"))
{
    int price = (int)table["apple"]!; // object → int のキャスト
    Console.WriteLine(price);
}

Dictionary<TKey, TValue> がチェイニング(バケット内に連結リスト)を採用しているのに対し、Hashtable は オープンアドレス法 を採用しているのが実装上の大きな違いです。

サポートするインターフェース

インターフェース 役割
IDictionary キー・値の組を扱う連想コレクション
ICollection Count / CopyTo / 同期サポート
IEnumerable foreach で DictionaryEntry を列挙
ICloneable Clone() で浅いコピー

IDictionary

IDictionary は キーと値の対応づけ を表す非ジェネリックなインターフェースです。Hashtable の中核となる API はすべてここで定義されています。

public interface IDictionary : ICollection
{
    object? this[object key] { get; set; }
    ICollection Keys { get; }
    ICollection Values { get; }
    bool Contains(object key);
    void Add(object key, object? value);
    void Remove(object key);
    void Clear();
    IDictionaryEnumerator GetEnumerator();
    bool IsFixedSize { get; }
    bool IsReadOnly { get; }
}

foreach で得られる要素は DictionaryEntry 構造体で、Key と Value プロパティを持ちます。

foreach (DictionaryEntry entry in table)
{
    Console.WriteLine($"{entry.Key} = {entry.Value}");
}

IDictionary を実装するメリットは、構成ファイルやテンプレートエンジンなど キー・値構造を要求する API にそのまま渡せる点です(例:HashtableConverter、古い ASP.NET の ViewState まわりなど)。

ICollection / IEnumerable / ICloneable

  • ICollection の詳細は ArrayList の記事 を参照。
  • IEnumerable / IEnumerator の詳細は IEnumerable と IEnumerator の記事 を参照。
  • ICloneable の浅いコピーと深いコピーの違いは ICloneable インターフェースの記事 を参照。

Hashtable.Clone() は 浅いコピー。バケット配列は新規確保されますが、参照型のキー・値は同じインスタンスを共有します。

キーの等値性 — GetHashCode と Equals

Hashtable がキーを格納する際は、

  1. key.GetHashCode() でハッシュ値を計算
  2. ハッシュ値からバケット位置を決定
  3. 衝突したら別のキーと key.Equals(other) で比較

という流れになります。キーに使う型は GetHashCode と Equals を一貫して実装 しておく必要があります。record 型や値の等価性で比較したい型では特に重要です。

カスタムの比較ロジックを使いたいときは、コンストラクタに IEqualityComparer を渡せます。

var caseInsensitive = new Hashtable(StringComparer.OrdinalIgnoreCase);
caseInsensitive["Apple"] = 1;
Console.WriteLine(caseInsensitive["APPLE"]); // 1

主な API と計算量

操作 API 平均計算量 最悪計算量
追加 Add(key, value) / [k] = v O(1) 償却 O(n)(再ハッシュ時)
取得 [key] O(1) O(n)(衝突時)
削除 Remove(key) O(1) O(n)
検索 ContainsKey(key) O(1) O(n)
値検索 ContainsValue(value) O(n) O(n)
列挙 foreach O(n)

ロードファクター(既定 1.0)を下げると 衝突は減るがメモリは増える。コンストラクタで指定可能です。

var table = new Hashtable(capacity: 10_000, loadFactor: 0.7f);

ボックス化のコスト

Dictionary<TKey, TValue> と異なり、Hashtable のキー・値は両方 object。値型を扱うとキー側・値側の両方で ボックス化 が発生します。

var ht = new Hashtable();
for (int i = 0; i < 1_000_000; i++) ht[i] = i; // キーも値も各 100 万回ボックス化

100 万件規模で Dictionary<int, int> と比べると、メモリ消費・速度の両面で 数倍~10 倍程度の差 が出ることもあります。

Dictionary<TKey, TValue> との比較

観点 Hashtable Dictionary<TKey, TValue>
型安全 × object のキャスト ◯ コンパイル時に保証
値型のボックス化 発生する 発生しない
衝突解決 オープンアドレス法 分離チェイニング
列挙の戻り値 DictionaryEntry KeyValuePair<TKey, TValue>
スレッドセーフ Synchronized ラッパあり(限定的) 通常版は非スレッドセーフ
推奨度 レガシーのみ 新規コードの標準

新規コードでは 常に Dictionary<TKey, TValue>、並行アクセスが必要なら ConcurrentDictionary<TKey, TValue> を選ぶのが鉄則です。

スレッドセーフラッパ

複数スレッドからの 読み取り専用アクセスはロックなしでスレッドセーフ、書き込みを含む並行アクセスは Hashtable.Synchronized でラップします。

var sync = Hashtable.Synchronized(new Hashtable());
sync["key"] = "value"; // 各操作が暗黙でロックされる

ただし、現代の並行処理では ConcurrentDictionary<TKey, TValue> のほうが粒度の細かいロックで高性能です。

使いどころ

実務で Hashtable を選ぶ理由はほぼありません。出会うのは次のような場面です。

  1. レガシーコードのメンテナンス:既存 API のシグネチャが Hashtable のまま。
  2. 古い ASP.NET / .NET Remoting / WCF:内部で Hashtable を使っている API への接続。
  3. 構成ファイル系の互換:古い IDictionary ベースの構成データ。
  4. 動的キー・値(型混在):理屈上は Dictionary<object, object> で十分。

移行例

// Before
Hashtable ht = LoadLegacy();
foreach (DictionaryEntry e in ht)
{
    string key   = (string)e.Key;
    int    value = (int)e.Value!;
    Console.WriteLine($"{key} = {value}");
}

// After(型がわかるなら直接 Dictionary に詰め替える)
var dict = ht.Cast<DictionaryEntry>()
             .ToDictionary(e => (string)e.Key, e => (int)e.Value!);

foreach (var (k, v) in dict)
    Console.WriteLine($"{k} = {v}");

まとめ

  • Hashtable は 非ジェネリックな連想配列。内部はオープンアドレス法のハッシュテーブル。
  • IDictionary / ICollection / IEnumerable / ICloneable を実装。
  • 平均 O(1) アクセスだが、値型は キー・値とも にボックス化される。
  • 新規コードでは Dictionary<TKey, TValue>、並行なら ConcurrentDictionary<TKey, TValue> を選ぶ。
  • レガシー API との接続点で出会ったら Cast<DictionaryEntry>().ToDictionary(...) で素早く型安全に持ち込もう。
C# .NET Hashtable System.Collections ハッシュテーブル コレクション
← [C#] System.Collections.BitArray — ビット列を効率的に扱う固定長コレクション [C#] System.Collections.Queue — 非ジェネリックな FIFO キューの仕組みと使いどころ →

Related Posts

  • [C#] System.Collections.Stack — 非ジェネリックな LIFO スタックの仕組みと使いどころ May 20, 2026
  • [C#] System.Collections.SortedList — キーで自動整列される連想配列の仕組みと使いどころ May 19, 2026
  • [C#] System.Collections.Queue — 非ジェネリックな FIFO キューの仕組みと使いどころ May 18, 2026
  • [C#] System.Collections.ArrayList — 可変長配列(非ジェネリック)の仕組みと使いどころ May 15, 2026

Table of Contents

  • Hashtable とは
  • サポートするインターフェース
    • IDictionary
    • ICollection / IEnumerable / ICloneable
  • キーの等値性 — GetHashCode と Equals
  • 主な API と計算量
  • ボックス化のコスト
  • Dictionary<TKey, TValue> との比較
  • スレッドセーフラッパ
  • 使いどころ
  • 移行例
  • まとめ

Recent Posts

  • [C#] System.Collections.Specialized.ListDictionary — 小規模辞書に特化した連結リスト実装 May 22, 2026
  • [C#] System.Collections.Specialized.HybridDictionary — 小規模では ListDictionary、大規模では Hashtable May 21, 2026
  • [C#] System.Collections.Stack — 非ジェネリックな LIFO スタックの仕組みと使いどころ May 20, 2026
  • [C#] System.Collections.SortedList — キーで自動整列される連想配列の仕組みと使いどころ May 19, 2026
  • [C#] System.Collections.Queue — 非ジェネリックな FIFO キューの仕組みと使いどころ May 18, 2026

Categories

  • C#72
  • .NET71
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • コレクション
  • Apache
  • パフォーマンス
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • DoS
  • Nginx
  • Windows
  • WordPress
  • メモリ管理
  • 監視
  • 設計
  • Amazon Linux 2023
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • OPCache
  • Webサーバー
  • オブジェクト指向
  • クラス設計
  • デザインパターン
  • パターンマッチング
  • 継承
  • 認可
  • Aurora
  • Blade
  • Grafana
  • Hugo
  • InfluxDB
  • Policy
  • Record
  • SSG
  • インターフェース
  • エラーハンドリング
  • カプセル化
  • ガベージコレクション
  • モニタリング
Powered by Hugo & Explore Theme.