C# Dictionary の初期化 - 様々な生成方法まとめ

C# で Dictionary を使うには、まずインスタンスを生成する必要があります。用途や状況に応じて複数の初期化方法が用意されており、それぞれ書き方や特性が異なります。

コンストラクタによる基本的な初期化

最もシンプルな方法は、new キーワードでコンストラクタを呼び出す書き方です。型パラメータにキーの型と値の型を指定します。

var dict = new Dictionary<string, int>();

この時点では要素が 1 つも入っていない空の辞書が生成されます。あとから Add メソッドや インデクサ で要素を追加していく使い方が一般的です。

要素数がある程度わかっている場合は、コンストラクタに初期容量を渡すことができます。

var dict = new Dictionary<string, int>(100);

内部的には、Dictionary はハッシュテーブルで実装されています。要素が増えるたびに内部配列のリサイズが発生すると、その都度コストがかかります。あらかじめ容量を確保しておけば、リサイズの回数を抑えられるため、大量の要素を扱う場面ではパフォーマンス面で有利になります。

コレクション初期化子

宣言と同時に要素を設定したい場合は、コレクション初期化子を使います。波括弧の中にキーと値のペアを並べる書き方です。

var capitals = new Dictionary<string, string>
{
    { "Japan", "Tokyo" },
    { "France", "Paris" },
    { "Germany", "Berlin" }
};

この構文は内部的に Add メソッドを繰り返し呼び出しているのと同じ動作になります。そのため、重複するキーを含めるとコンパイルは通りますが、実行時に ArgumentException がスローされます。

インデクス初期化子

C# 6.0 以降では、インデクス初期化子と呼ばれる書き方も使えます。角括弧でキーを指定し、= で値を代入する形式です。

var capitals = new Dictionary<string, string>
{
    ["Japan"] = "Tokyo",
    ["France"] = "Paris",
    ["Germany"] = "Berlin"
};

見た目の違いだけでなく、動作にも差があります。コレクション初期化子が Add を呼ぶのに対し、インデクス初期化子はインデクサ経由の代入です。つまり、同じキーが複数回出現した場合、例外にはならず後勝ちで上書きされます。

コレクション初期化子

内部で Add を呼ぶため、重複キーがあると ArgumentException がスローされる。キーの一意性を厳密に保証したい場面に向いている。

インデクス初期化子

インデクサ代入なので重複キーは後勝ちで上書きされる。同じキーに対して値を差し替える可能性がある場面で便利。

どちらを使っても最終的に得られる Dictionary の性能や機能は同じです。チーム内でスタイルを統一しておくと可読性が保たれます。

既存の Dictionary からコピーする

別の Dictionary を引数に渡すと、その内容をコピーした新しいインスタンスが作られます。

var original = new Dictionary<string, int>
{
    ["Alice"] = 90,
    ["Bob"] = 85
};

var copy = new Dictionary<string, int>(original);

この方法で生成される辞書はシャローコピーです。値が参照型の場合、コピー先とコピー元が同じオブジェクトを参照している点には注意が必要です。値型であれば独立したコピーになるため、片方を変更してももう一方には影響しません。

KeyValuePair の配列や IEnumerable から生成する

.NET Core 2.0 以降では、KeyValuePair のシーケンスを渡して Dictionary を生成できます。

var pairs = new[]
{
    new KeyValuePair<string, int>("X", 1),
    new KeyValuePair<string, int>("Y", 2)
};

var dict = new Dictionary<string, int>(pairs);

また、LINQ の ToDictionary メソッドを使えば、任意のコレクションから Dictionary を動的に組み立てることもできます。

var names = new[] { "Alice", "Bob", "Charlie" };
var dict = names.ToDictionary(
    name => name,
    name => name.Length
);

この例では、名前をキー、文字数を値とする辞書が生成されます。元のコレクションにキーの重複がある場合は ArgumentException になるため、事前に Distinct などで重複を除いておく必要があります。

IEqualityComparer を指定する

コンストラクタの引数に IEqualityComparer を渡すと、キーの比較方法をカスタマイズできます。

var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
    ["apple"] = 1
};

bool exists = dict.ContainsKey("APPLE"); // true

StringComparer.OrdinalIgnoreCase を指定すると、大文字・小文字の違いを無視してキーを照合します。HTTP ヘッダーや設定ファイルのキーなど、大文字小文字を区別したくない場面で役立つ機能です。この比較子はコレクション初期化子やインデクス初期化子とも組み合わせて使えます。