C# Dictionaryの使い方を完全解説|宣言・追加・検索・削除・ループ処理まで

はじめに

C#でアプリケーションを開発していると、「ユーザーIDからユーザー名を取得したい」「商品コードから価格を調べたい」「文字列ごとの出現回数を数えたい」といった場面がよくあります。このように、あるキーをもとに対応する値を素早く取得したい場合に便利なのがDictionaryです。

C#のDictionaryは、キーと値のペアでデータを管理するコレクションです。配列やListのように順番でデータを探すのではなく、キーを指定して目的の値にアクセスできるため、検索処理を効率よく書けます。

この記事では、C# Dictionaryの基本から、宣言、追加、更新、検索、削除、ループ処理、LINQとの組み合わせ、実践的な使い方、よくあるエラーの対処法まで詳しく解説します。

1. C#のDictionaryとは

1-1. Dictionaryの基本概念

C#のDictionary<TKey, TValue>は、キーと値をセットで管理するためのコレクションです。TKeyにはキーの型、TValueには値の型を指定します。

たとえば、ユーザーIDとユーザー名を管理する場合は、次のように考えられます。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

この例では、int型のユーザーIDをキーにして、string型のユーザー名を値として管理します。

Dictionaryは、キーを指定すると対応する値を高速に取得できる点が特徴です。そのため、データ検索を頻繁に行う処理でよく使われます。

1-2. キーと値でデータを管理する仕組み

Dictionaryでは、1つのキーに対して1つの値を関連付けます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores.Add("Alice", 90);
scores.Add("Bob", 75);
scores.Add("Charlie", 82);

この場合、"Alice"というキーには90"Bob"というキーには75が対応しています。

値を取得するときは、キーを指定します。

C#
Console.WriteLine(scores["Alice"]);

実行結果は次のようになります。

C#
90

このように、Dictionaryでは「名前から点数を取得する」「商品コードから価格を取得する」など、意味のあるキーを使ってデータを扱えます。

1-3. 配列・List・Hashtableとの違い

C#には複数のコレクションがありますが、Dictionaryはキーによる高速検索に向いています。

配列は、固定長のデータをインデックスで管理します。

C#
string[] names = { "Alice", "Bob", "Charlie" };
Console.WriteLine(names[0]);

Listは可変長のデータを順番に管理します。

C#
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
Console.WriteLine(names[1]);

一方、Dictionaryはインデックス番号ではなく、キーで値を管理します。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

Console.WriteLine(users[2]);

Hashtableもキーと値を扱えますが、型安全ではありません。Dictionary<TKey, TValue>はキーと値の型を明確に指定できるため、現在のC#ではHashtableよりもDictionaryがよく使われます。

C#
// Dictionaryは型が明確
Dictionary<int, string> users = new Dictionary<int, string>();

// Hashtableはobject型で扱われる
Hashtable table = new Hashtable();

基本的には、特別な理由がない限りHashtableではなくDictionaryを使うのがおすすめです。

1-4. Dictionaryを使うメリットと代表的な用途

Dictionaryを使う主なメリットは、キーを使って値を高速に検索できることです。また、キーと値の関係が明確になるため、コードの意図も読みやすくなります。

代表的な用途には、次のようなものがあります。

ユーザーIDとユーザー名の管理、商品コードと価格の管理、単語の出現回数のカウント、設定名と設定値の管理、エラーコードとメッセージの対応表、都道府県コードと都道府県名の管理などです。

たとえば、エラーコードとメッセージを管理する場合は次のように書けます。

C#
Dictionary<int, string> errorMessages = new Dictionary<int, string>
{
{ 404, "ページが見つかりません" },
{ 500, "サーバーエラーが発生しました" },
{ 403, "アクセスが拒否されました" }
};

Console.WriteLine(errorMessages[404]);

Dictionaryを使うことで、条件分岐を大量に書かずに、キーから対応する値を簡潔に取得できます。

2. Dictionaryの宣言と初期化

2-1. Dictionaryの基本的な宣言方法

Dictionaryの基本的な宣言方法は次のとおりです。

C#
Dictionary<TKey, TValue> 変数名 = new Dictionary<TKey, TValue>();

実際のコードでは、キーと値の型を指定します。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

この例では、キーがint型、値がstring型のDictionaryを作成しています。

文字列をキーにして数値を管理する場合は、次のように書きます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

Dictionaryはジェネリック型なので、キーと値の型を自由に組み合わせられます。

2-2. using System.Collections.Genericの指定

Dictionaryを使うには、通常ファイルの先頭に次のusingを記述します。

C#
using System.Collections.Generic;

この名前空間には、DictionaryのほかにListQueueStackなどのジェネリックコレクションが含まれています。

C#
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
Dictionary<int, string> users = new Dictionary<int, string>();
users.Add(1, "Alice");

Console.WriteLine(users[1]);
}
}

最近のC#プロジェクトでは、テンプレートによって暗黙的にusingが有効になっている場合もあります。ただし、明示的に書く場合はSystem.Collections.Genericを指定します。

2-3. 文字列・数値・オブジェクトを扱うDictionaryの例

Dictionaryでは、キーにも値にもさまざまな型を指定できます。

文字列をキー、文字列を値にする例です。

C#
Dictionary<string, string> capitals = new Dictionary<string, string>();

capitals.Add("Japan", "Tokyo");
capitals.Add("France", "Paris");
capitals.Add("Italy", "Rome");

文字列をキー、数値を値にする例です。

C#
Dictionary<string, int> prices = new Dictionary<string, int>();

prices.Add("Apple", 150);
prices.Add("Banana", 100);
prices.Add("Orange", 120);

数値をキー、オブジェクトを値にすることもできます。

C#
class User
{
public int Id { get; set; }
public string Name { get; set; }
}

Dictionary<int, User> users = new Dictionary<int, User>();

users.Add(1, new User { Id = 1, Name = "Alice" });
users.Add(2, new User { Id = 2, Name = "Bob" });

このように、Dictionaryは単純な値だけでなく、クラスのインスタンスも扱えます。

2-4. 初期値を指定してDictionaryを作成する方法

Dictionaryは、作成時に初期値を指定できます。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

この書き方を使うと、宣言と同時に要素を登録できます。

インデクサー形式で初期化することもできます。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
[1] = "Alice",
[2] = "Bob",
[3] = "Charlie"
};

どちらもよく使われますが、同じキーが重複しないように注意が必要です。

2-5. varを使ったDictionaryの宣言

C#では、型が明確に推論できる場合にvarを使えます。

C#
var users = new Dictionary<int, string>();

初期値を指定する場合も、varで宣言できます。

C#
var scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 75 },
{ "Charlie", 82 }
};

varを使うとコードが短くなります。ただし、型が分かりにくくなる場合は、明示的にDictionary<int, string>のように書いたほうが読みやすいこともあります。

3. Dictionaryに要素を追加・更新する方法

3-1. Addメソッドで要素を追加する

Dictionaryに要素を追加するには、Addメソッドを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users.Add(1, "Alice");
users.Add(2, "Bob");
users.Add(3, "Charlie");

Addメソッドでは、第1引数にキー、第2引数に値を指定します。

C#
users.Add(4, "David");

追加した値は、キーを指定して取得できます。

C#
Console.WriteLine(users[4]);

実行結果は次のようになります。

C#
David

Addは、存在しないキーに新しい要素を追加したい場合に使います。

3-2. インデクサーを使って要素を追加する

Dictionaryでは、インデクサーを使って要素を追加することもできます。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users[1] = "Alice";
users[2] = "Bob";

この書き方では、指定したキーが存在しない場合は新しく追加されます。

C#
users[3] = "Charlie";

Addよりも短く書けるため、シンプルに追加・更新したい場合によく使われます。

3-3. 既存キーの値を更新する

既存キーの値を更新する場合は、インデクサーを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" }
};

users[2] = "Robert";

Console.WriteLine(users[2]);

実行結果は次のようになります。

C#
Robert

このように、既に存在するキーに対してインデクサーで値を代入すると、値が上書きされます。

3-4. Addとインデクサーの違い

Addとインデクサーはどちらも要素を追加できますが、重複キーに対する動作が異なります。

Addは、同じキーがすでに存在する場合に例外を発生させます。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users.Add(1, "Alice");
users.Add(1, "Bob"); // エラー

一方、インデクサーは、同じキーが存在する場合に値を上書きします。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users[1] = "Alice";
users[1] = "Bob";

Console.WriteLine(users[1]);

実行結果は次のようになります。

C#
Bob

新規追加だけを許可したい場合はAdd、追加と更新をまとめて行いたい場合はインデクサーを使うとよいでしょう。

3-5. 重複キーを追加した場合のエラー対策

Addで重複キーを追加すると、次のような例外が発生します。

C#
An item with the same key has already been added.

このエラーを防ぐには、ContainsKeyで事前にキーの存在を確認します。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

if (!users.ContainsKey(1))
{
users.Add(1, "Alice");
}

また、キーが存在する場合は更新し、存在しない場合は追加するなら、インデクサーを使う方法が簡単です。

C#
users[1] = "Alice";

C#のバージョンや利用環境によっては、TryAddを使うこともできます。

C#
bool added = users.TryAdd(1, "Alice");

if (added)
{
Console.WriteLine("追加しました");
}
else
{
Console.WriteLine("すでに同じキーが存在します");
}

重複を許可したくない場合はAddTryAdd、上書きしてよい場合はインデクサーを使い分けましょう。

4. Dictionaryから値を検索・取得する方法

4-1. キーを指定して値を取得する

Dictionaryから値を取得する基本的な方法は、キーを指定することです。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

string name = users[2];

Console.WriteLine(name);

実行結果は次のようになります。

C#
Bob

インデクサーを使うと簡潔に値を取得できますが、存在しないキーを指定すると例外が発生します。

C#
Console.WriteLine(users[10]); // エラー

そのため、キーが存在するか分からない場合は、ContainsKeyTryGetValueを使うのが安全です。

4-2. ContainsKeyでキーの存在を確認する

ContainsKeyは、指定したキーがDictionaryに存在するかどうかを確認するメソッドです。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" }
};

if (users.ContainsKey(1))
{
Console.WriteLine(users[1]);
}
else
{
Console.WriteLine("指定したキーは存在しません");
}

ContainsKeyは、キーの存在を確認してから値を取得したい場合に便利です。

ただし、ContainsKeyで確認した後にインデクサーで取得すると、内部的には検索が2回行われます。パフォーマンスを意識する場合は、次に紹介するTryGetValueがおすすめです。

4-3. TryGetValueで安全に値を取得する

TryGetValueは、キーが存在する場合に値を取得し、存在しない場合でも例外を発生させない便利なメソッドです。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" }
};

if (users.TryGetValue(2, out string name))
{
Console.WriteLine(name);
}
else
{
Console.WriteLine("指定したキーは存在しません");
}

実行結果は次のようになります。

C#
Bob

キーが存在しない場合は、falseが返ります。

C#
if (users.TryGetValue(10, out string name))
{
Console.WriteLine(name);
}
else
{
Console.WriteLine("見つかりませんでした");
}

TryGetValueは、キーの存在確認と値の取得を同時に行えるため、実務でもよく使われる書き方です。

4-4. ContainsValueで値の存在を確認する

ContainsValueを使うと、指定した値がDictionaryに含まれているかを確認できます。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

bool exists = users.ContainsValue("Bob");

Console.WriteLine(exists);

実行結果は次のようになります。

C#
True

ただし、Dictionaryはキー検索に強いコレクションです。値を検索するContainsValueは、キー検索ほど高速ではありません。

頻繁に値から検索する必要がある場合は、データ構造を見直すことも検討しましょう。

4-5. キーが存在しない場合の例外と回避方法

存在しないキーをインデクサーで取得しようとすると、例外が発生します。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" }
};

Console.WriteLine(users[99]); // エラー

この場合、次のようなエラーになります。

C#
The given key was not present in the dictionary.

回避するには、ContainsKeyを使います。

C#
if (users.ContainsKey(99))
{
Console.WriteLine(users[99]);
}
else
{
Console.WriteLine("キーが存在しません");
}

よりおすすめなのは、TryGetValueです。

C#
if (users.TryGetValue(99, out string name))
{
Console.WriteLine(name);
}
else
{
Console.WriteLine("キーが存在しません");
}

キーが存在するか分からない場合は、直接users[key]で取得せず、TryGetValueを使う習慣をつけると安全です。

5. Dictionaryの要素を削除する方法

5-1. Removeメソッドで指定したキーを削除する

Dictionaryから要素を削除するには、Removeメソッドを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

users.Remove(2);

このコードでは、キー2に対応する要素が削除されます。

C#
foreach (var user in users)
{
Console.WriteLine($"{user.Key}: {user.Value}");
}

実行結果は次のようになります。

C#
1: Alice
3: Charlie

Removeにはキーを指定します。値を指定して削除するわけではない点に注意しましょう。

5-2. Clearメソッドで全要素を削除する

Dictionaryのすべての要素を削除したい場合は、Clearメソッドを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

users.Clear();

Console.WriteLine(users.Count);

実行結果は次のようになります。

C#
0

Clearを使うと、Dictionary自体は残したまま、中身の要素だけをすべて削除できます。

5-3. 削除に成功したかどうかを判定する

Removeメソッドは、削除に成功したかどうかをboolで返します。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" }
};

bool removed = users.Remove(2);

if (removed)
{
Console.WriteLine("削除しました");
}
else
{
Console.WriteLine("指定したキーは存在しません");
}

存在しないキーを指定した場合、例外は発生せずfalseが返ります。

C#
bool removed = users.Remove(99);
Console.WriteLine(removed);

実行結果は次のようになります。

C#
False

削除できたかどうかを確認したい場合は、Removeの戻り値を使いましょう。

5-4. ループ中に削除する場合の注意点

foreachDictionaryをループしている最中に、直接要素を削除するとエラーになります。

C#
foreach (var item in users)
{
if (item.Key == 2)
{
users.Remove(item.Key); // エラー
}
}

この場合、コレクションの内容を列挙中に変更しているため、例外が発生します。

安全に削除するには、削除対象のキーを一度リストにしてから削除します。

C#
var keysToRemove = users
.Where(x => x.Key == 2)
.Select(x => x.Key)
.ToList();

foreach (var key in keysToRemove)
{
users.Remove(key);
}

ToListで削除対象を別のリストにしておけば、元のDictionaryをループ中に変更する問題を避けられます。

6. Dictionaryをループ処理する方法

6-1. foreachでキーと値を取り出す

Dictionaryの要素を順番に処理するには、foreachを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

foreach (var user in users)
{
Console.WriteLine($"{user.Key}: {user.Value}");
}

foreachで取り出される要素は、キーと値を持つKeyValuePair<TKey, TValue>です。

実行結果は次のようになります。

C#
1: Alice
2: Bob
3: Charlie

キーはKeyプロパティ、値はValueプロパティで取得できます。

6-2. Keysプロパティでキーだけをループする

キーだけを処理したい場合は、Keysプロパティを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

foreach (int key in users.Keys)
{
Console.WriteLine(key);
}

実行結果は次のようになります。

C#
1
2
3

キーを使って値を取得することもできます。

C#
foreach (int key in users.Keys)
{
Console.WriteLine($"{key}: {users[key]}");
}

ただし、キーと値の両方を使うなら、通常はforeach (var item in users)のほうが自然です。

6-3. Valuesプロパティで値だけをループする

値だけを処理したい場合は、Valuesプロパティを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

foreach (string name in users.Values)
{
Console.WriteLine(name);
}

実行結果は次のようになります。

C#
Alice
Bob
Charlie

値だけを表示したい場合や、合計値を求めたい場合などに便利です。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 75 },
{ "Charlie", 82 }
};

int total = 0;

foreach (int score in scores.Values)
{
total += score;
}

Console.WriteLine(total);

6-4. KeyValuePairを使ったループ処理

Dictionaryの各要素はKeyValuePair<TKey, TValue>として扱われます。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" }
};

foreach (KeyValuePair<int, string> user in users)
{
Console.WriteLine($"Key={user.Key}, Value={user.Value}");
}

varを使うことも多いですが、KeyValuePairを明示すると、Dictionaryの構造を理解しやすくなります。

C#
foreach (var user in users)
{
Console.WriteLine(user.Key);
Console.WriteLine(user.Value);
}

キーと値を同時に扱う処理では、この書き方が基本です。

6-5. LINQと組み合わせて絞り込み・変換する

DictionaryはLINQと組み合わせることで、絞り込みや変換を簡潔に書けます。

たとえば、80点以上のデータだけを取得する場合は次のように書けます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 75 },
{ "Charlie", 82 }
};

var highScores = scores.Where(x => x.Value >= 80);

foreach (var item in highScores)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

実行結果は次のようになります。

C#
Alice: 90
Charlie: 82

値を変換して新しいコレクションを作ることもできます。

C#
var names = scores.Select(x => x.Key).ToList();

LINQを使うには、通常次のusingを追加します。

C#
using System.Linq;

7. Dictionaryでよく使う便利な操作

7-1. Countで要素数を取得する

Dictionaryの要素数を取得するには、Countプロパティを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" },
{ 3, "Charlie" }
};

Console.WriteLine(users.Count);

実行結果は次のようになります。

C#
3

Countは、要素が何件あるかを確認したいときによく使います。

C#
if (users.Count > 0)
{
Console.WriteLine("ユーザーが登録されています");
}

7-2. Anyで要素の有無を確認する

LINQのAnyを使うと、要素が存在するかどうかを確認できます。

C#
using System.Linq;

Dictionary<int, string> users = new Dictionary<int, string>();

if (users.Any())
{
Console.WriteLine("要素があります");
}
else
{
Console.WriteLine("要素がありません");
}

条件を指定して確認することもできます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "Alice", 90 },
{ "Bob", 75 }
};

bool hasHighScore = scores.Any(x => x.Value >= 80);

Console.WriteLine(hasHighScore);

実行結果は次のようになります。

C#
True

単純に要素数を確認するだけならCount > 0でも問題ありませんが、条件付きの判定にはAnyが便利です。

7-3. ToDictionaryでListからDictionaryに変換する

ListからDictionaryに変換するには、LINQのToDictionaryを使います。

C#
class User
{
public int Id { get; set; }
public string Name { get; set; }
}

List<User> userList = new List<User>
{
new User { Id = 1, Name = "Alice" },
new User { Id = 2, Name = "Bob" },
new User { Id = 3, Name = "Charlie" }
};

Dictionary<int, User> userDictionary = userList.ToDictionary(user => user.Id);

この例では、UserオブジェクトのIdをキーにしたDictionaryを作成しています。

値を特定のプロパティにすることもできます。

C#
Dictionary<int, string> names = userList.ToDictionary(
user => user.Id,
user => user.Name
);

ToDictionaryを使う場合、キーが重複すると例外が発生するため注意が必要です。

7-4. DictionaryをListに変換する

DictionaryListに変換したい場合は、ToListを使います。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "Alice" },
{ 2, "Bob" }
};

var userList = users.ToList();

この場合、userListの要素はKeyValuePair<int, string>になります。

キーだけをリストにすることもできます。

C#
List<int> keys = users.Keys.ToList();

値だけをリストにすることもできます。

C#
List<string> values = users.Values.ToList();

オブジェクトのリストに変換したい場合は、Selectを使います。

C#
var list = users.Select(x => new
{
Id = x.Key,
Name = x.Value
}).ToList();

7-5. キーや値でDictionaryを並び替える

Dictionaryそのものは、並び順に依存する設計には向いていません。ただし、表示や処理のために一時的に並び替えることはできます。

キーで昇順に並び替える例です。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 3, "Charlie" },
{ 1, "Alice" },
{ 2, "Bob" }
};

var sortedByKey = users.OrderBy(x => x.Key);

foreach (var item in sortedByKey)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

値で昇順に並び替える例です。

C#
var sortedByValue = users.OrderBy(x => x.Value);

降順にしたい場合は、OrderByDescendingを使います。

C#
var sortedDesc = users.OrderByDescending(x => x.Key);

並び替えた結果を再びDictionaryにすることもできます。

C#
var sortedDictionary = users
.OrderBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Value);

ただし、順序を重視するならSortedDictionarySortedListも検討しましょう。

8. Dictionaryの実践的な使い方

8-1. ユーザーIDとユーザー名を管理する例

Dictionaryの代表的な使い方として、ユーザーIDとユーザー名の管理があります。

C#
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1001, "Alice" },
{ 1002, "Bob" },
{ 1003, "Charlie" }
};

int userId = 1002;

if (users.TryGetValue(userId, out string userName))
{
Console.WriteLine($"ユーザー名: {userName}");
}
else
{
Console.WriteLine("ユーザーが見つかりません");
}

ユーザーIDのように一意な値をキーにすると、目的のユーザー情報を簡単に取得できます。

8-2. 商品コードと価格を管理する例

商品コードと価格を管理する場合にもDictionaryは便利です。

C#
Dictionary<string, int> productPrices = new Dictionary<string, int>
{
{ "A001", 1200 },
{ "B002", 2500 },
{ "C003", 980 }
};

string productCode = "B002";

if (productPrices.TryGetValue(productCode, out int price))
{
Console.WriteLine($"価格: {price}円");
}
else
{
Console.WriteLine("商品が見つかりません");
}

商品コードをキーにすることで、価格表のようなデータを簡単に扱えます。

価格を更新する場合は、インデクサーを使います。

C#
productPrices["B002"] = 2300;

8-3. 文字列の出現回数をカウントする例

Dictionaryは、文字列や単語の出現回数をカウントする処理にもよく使われます。

C#
string[] words = { "apple", "banana", "apple", "orange", "banana", "apple" };

Dictionary<string, int> counts = new Dictionary<string, int>();

foreach (string word in words)
{
if (counts.ContainsKey(word))
{
counts[word]++;
}
else
{
counts[word] = 1;
}
}

foreach (var item in counts)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

実行結果は次のようになります。

C#
apple: 3
banana: 2
orange: 1

TryGetValueを使うと、次のようにも書けます。

C#
foreach (string word in words)
{
if (counts.TryGetValue(word, out int count))
{
counts[word] = count + 1;
}
else
{
counts[word] = 1;
}
}

文字の頻度、単語の集計、ログの分類などに応用できます。

8-4. オブジェクトを値に持つDictionaryの例

Dictionaryの値には、クラスのインスタンスを指定できます。

C#
class Product
{
public string Code { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}

Dictionary<string, Product> products = new Dictionary<string, Product>
{
{
"A001",
new Product { Code = "A001", Name = "キーボード", Price = 3000 }
},
{
"B002",
new Product { Code = "B002", Name = "マウス", Price = 1500 }
}
};

if (products.TryGetValue("A001", out Product product))
{
Console.WriteLine($"{product.Name}: {product.Price}円");
}

このように、商品コードをキーにして、商品情報全体を値として管理できます。

値がオブジェクトになると、複数の情報をまとめて扱えるため、実務での利用範囲が広がります。

8-5. 入れ子のDictionaryを使う方法

Dictionaryの値として、さらにDictionaryを持たせることもできます。これを入れ子のDictionaryと呼ぶことがあります。

C#
Dictionary<string, Dictionary<string, int>> scores =
new Dictionary<string, Dictionary<string, int>>
{
{
"Alice",
new Dictionary<string, int>
{
{ "Math", 90 },
{ "English", 85 }
}
},
{
"Bob",
new Dictionary<string, int>
{
{ "Math", 75 },
{ "English", 80 }
}
}
};

Console.WriteLine(scores["Alice"]["Math"]);

実行結果は次のようになります。

C#
90

入れ子のDictionaryは、カテゴリ別のデータ管理に使えます。ただし、階層が深くなりすぎるとコードが読みにくくなるため、必要に応じてクラスを作成したほうがよい場合もあります。

9. Dictionary使用時の注意点

9-1. キーは一意である必要がある

Dictionaryでは、キーは一意でなければなりません。同じキーを複数登録することはできません。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users.Add(1, "Alice");
users.Add(1, "Bob"); // エラー

同じキーに複数の値を持たせたい場合は、値をListにする方法があります。

C#
Dictionary<string, List<string>> groups = new Dictionary<string, List<string>>();

groups["A"] = new List<string> { "Alice", "Bob" };

キーの重複を許可したいのか、一意であるべきなのかを設計時に明確にしておきましょう。

9-2. nullをキーにできないケース

Dictionaryでは、キーにnullを指定できない場合があります。特に参照型をキーにしている場合、nullキーを追加しようとすると例外になります。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores.Add(null, 100); // エラー

値にはnullを入れられる場合があります。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users.Add(1, null);

ただし、nullを値として扱うと、値が未設定なのか、意図的にnullなのかが分かりにくくなることがあります。設計上の意味を明確にして使いましょう。

9-3. キーの大文字・小文字を区別する点

文字列をキーにする場合、通常のDictionary<string, TValue>では大文字と小文字が区別されます。

C#
Dictionary<string, int> scores = new Dictionary<string, int>();

scores["apple"] = 100;
scores["Apple"] = 200;

Console.WriteLine(scores.Count);

実行結果は次のようになります。

C#
2

"apple""Apple"は別のキーとして扱われます。

大文字・小文字を区別したくない場合は、StringComparer.OrdinalIgnoreCaseを指定します。

C#
Dictionary<string, int> scores =
new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

scores["apple"] = 100;
scores["Apple"] = 200;

Console.WriteLine(scores.Count);
Console.WriteLine(scores["APPLE"]);

この場合、大文字・小文字を無視して同じキーとして扱われます。

9-4. foreach中の追加・削除で発生するエラー

Dictionaryforeachでループしている最中に、要素を追加または削除すると例外が発生します。

C#
foreach (var item in users)
{
users.Add(99, "New User"); // エラー
}

削除の場合も同様です。

C#
foreach (var item in users)
{
users.Remove(item.Key); // エラー
}

対策として、追加や削除をループ後に行う、または対象のキーを一度リストにコピーしてから処理します。

C#
var keys = users.Keys.ToList();

foreach (var key in keys)
{
if (key == 2)
{
users.Remove(key);
}
}

コレクションを列挙中に変更しないことが重要です。

9-5. Dictionaryの順序に依存しない設計にする

Dictionaryは、基本的にキーによる検索を目的としたコレクションです。そのため、順序に依存する設計は避けるべきです。

表示時に順序が必要な場合は、OrderByで並び替えます。

C#
foreach (var item in users.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

常にキー順で管理したい場合は、SortedDictionaryの利用も検討できます。

Dictionaryを使うときは、「順番で取り出す」ことよりも「キーで高速に取得する」ことを重視しましょう。

10. Dictionaryと関連クラスの違い

10-1. DictionaryとSortedDictionaryの違い

Dictionaryは、キーによる高速な検索に向いたコレクションです。一方、SortedDictionaryは、キーの順序を保ちながらデータを管理します。

C#
SortedDictionary<int, string> users = new SortedDictionary<int, string>
{
{ 3, "Charlie" },
{ 1, "Alice" },
{ 2, "Bob" }
};

foreach (var item in users)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

実行結果は、キーの昇順になります。

C#
1: Alice
2: Bob
3: Charlie

検索速度を重視するならDictionary、キー順で管理したいならSortedDictionaryが選択肢になります。

10-2. DictionaryとSortedListの違い

SortedListもキー順にデータを管理するコレクションです。

C#
SortedList<int, string> users = new SortedList<int, string>
{
{ 3, "Charlie" },
{ 1, "Alice" },
{ 2, "Bob" }
};

SortedDictionaryと同じく、キー順に並んだ状態で扱えます。

一般的に、要素の追加や削除が多い場合はSortedDictionary、データ量が比較的少なくインデックスアクセスも使いたい場合はSortedListが選択肢になります。

ただし、通常のキー検索だけで十分な場合は、まずDictionaryを検討するとよいでしょう。

10-3. DictionaryとConcurrentDictionaryの違い

ConcurrentDictionaryは、複数のスレッドから同時にアクセスされる場面を想定したコレクションです。

通常のDictionaryは、複数スレッドから同時に書き込みを行うと安全ではありません。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

マルチスレッド環境で安全に追加や更新を行いたい場合は、ConcurrentDictionaryを使います。

C#
using System.Collections.Concurrent;

ConcurrentDictionary<int, string> users = new ConcurrentDictionary<int, string>();

users.TryAdd(1, "Alice");
users.TryUpdate(1, "Bob", "Alice");

並列処理や非同期処理で共有データを扱う場合は、ConcurrentDictionaryを検討しましょう。

10-4. 用途別にどのコレクションを選ぶべきか

通常のキー検索を高速に行いたい場合は、Dictionaryが適しています。

キー順でデータを管理したい場合は、SortedDictionarySortedListが候補になります。

複数スレッドから安全にアクセスしたい場合は、ConcurrentDictionaryを使います。

順番にデータを追加して処理するだけなら、Listのほうがシンプルです。

つまり、選び方の目安は次のようになります。

キーで値を高速に取得したいならDictionary、順序付きでキー管理したいならSortedDictionary、大量の追加削除よりも並び順とインデックスアクセスを重視するならSortedList、マルチスレッドで使うならConcurrentDictionary、単純な一覧ならListを選ぶとよいでしょう。

11. Dictionaryのパフォーマンスと使いどころ

11-1. Dictionaryの検索が高速な理由

Dictionaryの検索が高速なのは、内部的にハッシュテーブルの仕組みを使っているためです。

キーからハッシュ値を計算し、そのハッシュ値をもとに値の格納場所を探します。そのため、要素数が増えても、キーから値を見つける処理が比較的高速です。

たとえば、次のようにキーを指定して値を取得できます。

C#
string name = users[1001];

Listのように先頭から順番に探す必要がないため、大量のデータから特定の要素を検索する処理に向いています。

11-2. List検索との速度の違い

Listで特定のデータを検索する場合、通常は先頭から順番に比較して探します。

C#
var user = userList.FirstOrDefault(x => x.Id == 1001);

データ件数が少なければ問題になりにくいですが、件数が増えると検索コストが大きくなります。

一方、Dictionaryではキーを使って直接値を取得できます。

C#
var user = userDictionary[1001];

ユーザーIDや商品コードなど、一意なキーで頻繁に検索する場合は、ListよりDictionaryのほうが効率的です。

ただし、すべての場面でDictionaryが最適というわけではありません。順番に全件処理するだけならListのほうがシンプルです。

11-3. 大量データでDictionaryが有効なケース

Dictionaryは、大量データの中から特定の値を何度も検索するケースで特に有効です。

たとえば、次のようなケースです。

大量のユーザー情報からIDでユーザーを取得する、商品マスタから商品コードで価格を取得する、ログデータの種類ごとの件数を集計する、CSVデータをキーで突き合わせる、キャッシュとして計算済みの結果を保存するなどです。

例として、商品コードから商品情報を検索する場合を考えます。

C#
Dictionary<string, Product> productMap = products.ToDictionary(x => x.Code);

if (productMap.TryGetValue("A001", out Product product))
{
Console.WriteLine(product.Name);
}

このように、事前にDictionary化しておくことで、繰り返し検索する処理を効率化できます。

11-4. メモリ使用量に関する注意点

Dictionaryは高速な検索ができる一方で、内部的にハッシュテーブルを管理するため、Listよりも多くのメモリを使う場合があります。

少量のデータや、単純に順番に処理するだけのデータであれば、Listのほうが適していることもあります。

また、キーと値の両方を保持するため、巨大なデータを扱う場合はメモリ使用量にも注意が必要です。

C#
Dictionary<int, LargeObject> cache = new Dictionary<int, LargeObject>();

キャッシュ用途でDictionaryを使う場合は、不要になった要素を削除する、最大件数を決める、必要に応じてクリアするなどの対策を検討しましょう。

12. Dictionaryでよくあるエラーと対処法

12-1. An item with the same key has already been addedの原因

An item with the same key has already been addedは、同じキーをAddで追加しようとしたときに発生するエラーです。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

users.Add(1, "Alice");
users.Add(1, "Bob"); // エラー

対処法としては、追加前にContainsKeyで確認します。

C#
if (!users.ContainsKey(1))
{
users.Add(1, "Alice");
}

または、TryAddを使います。

C#
if (!users.TryAdd(1, "Alice"))
{
Console.WriteLine("同じキーがすでに存在します");
}

上書きしてよい場合は、インデクサーを使います。

C#
users[1] = "Alice";

重複キーを許可しない設計なのか、上書き可能な設計なのかを決めておくことが重要です。

12-2. The given key was not present in the dictionaryの原因

The given key was not present in the dictionaryは、存在しないキーをインデクサーで取得しようとしたときに発生します。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

Console.WriteLine(users[1]); // エラー

対処法は、TryGetValueを使うことです。

C#
if (users.TryGetValue(1, out string name))
{
Console.WriteLine(name);
}
else
{
Console.WriteLine("指定したキーは存在しません");
}

ContainsKeyで確認してから取得する方法もあります。

C#
if (users.ContainsKey(1))
{
Console.WriteLine(users[1]);
}

ただし、実務ではTryGetValueのほうが安全で効率的な場面が多いです。

12-3. Collection was modifiedの原因

Collection was modifiedは、foreachでコレクションを列挙している最中に、要素を追加または削除した場合に発生するエラーです。

C#
foreach (var item in users)
{
users.Remove(item.Key); // エラー
}

対処法は、削除対象を一度別のリストにコピーすることです。

C#
var keysToRemove = users
.Where(x => x.Value.StartsWith("A"))
.Select(x => x.Key)
.ToList();

foreach (var key in keysToRemove)
{
users.Remove(key);
}

追加処理も同様に、ループ中に直接行わず、必要なデータを一度別に保持してから処理すると安全です。

12-4. エラーを防ぐための実装パターン

Dictionaryのエラーを防ぐには、よく使う安全な実装パターンを覚えておくことが大切です。

値を安全に取得する場合は、TryGetValueを使います。

C#
if (dictionary.TryGetValue(key, out var value))
{
// 値が存在する場合の処理
}
else
{
// 値が存在しない場合の処理
}

重複キーを避けて追加する場合は、TryAddを使います。

C#
if (!dictionary.TryAdd(key, value))
{
// すでにキーが存在する場合の処理
}

上書きしてよい場合は、インデクサーを使います。

C#
dictionary[key] = value;

ループ中に削除する場合は、対象キーを先にリスト化します。

C#
foreach (var key in dictionary.Keys.ToList())
{
if (ShouldRemove(key))
{
dictionary.Remove(key);
}
}

これらのパターンを使うことで、C# Dictionaryでよくある例外を避けながら、安全で読みやすいコードを書けます。

まとめ

C#のDictionaryは、キーと値のペアでデータを管理できる便利なコレクションです。キーを指定して値を高速に取得できるため、ユーザーID、商品コード、設定値、集計データなど、さまざまな場面で活用できます。

基本的な宣言は次のように書きます。

C#
Dictionary<int, string> users = new Dictionary<int, string>();

要素の追加にはAddやインデクサーを使います。

C#
users.Add(1, "Alice");
users[2] = "Bob";

値を安全に取得するには、TryGetValueが便利です。

C#
if (users.TryGetValue(1, out string name))
{
Console.WriteLine(name);
}

削除にはRemove、全削除にはClearを使います。

C#
users.Remove(1);
users.Clear();

ループ処理では、foreachを使ってキーと値を取り出せます。

C#
foreach (var item in users)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}

Dictionaryを使うときは、キーが一意であること、存在しないキーを直接取得すると例外になること、ループ中に追加・削除しないこと、順序に依存しないことに注意しましょう。

C# Dictionaryを正しく使えるようになると、検索処理やデータ管理のコードを効率よく、分かりやすく書けるようになります。配列やListだけでなく、キーと値の関係で管理したいデータがある場合は、積極的にDictionaryを活用してみましょう。