C#にHashMapはある?Dictionaryの使い方・違い・実装例を初心者向けに解説
はじめに
「C#にHashMapはあるの?」「JavaのHashMapと同じようにキーと値でデータを管理したい」と考えている方は多いのではないでしょうか。
結論からいうと、C#にはJavaのHashMapという名前のクラスはありません。その代わりに、C#では主に**Dictionary<TKey, TValue>**を使います。
Dictionaryは、キーと値をセットで管理できるコレクションで、JavaのHashMapに近い役割を持っています。たとえば、ユーザーIDからユーザー名を取得したり、商品コードから価格を調べたり、文字列の出現回数をカウントしたりするときに便利です。
この記事では、C#初心者向けに「C#にHashMapはあるのか」という疑問から、Dictionaryの基本的な使い方、JavaのHashMapとの違い、実装例、注意点までわかりやすく解説します。
1. C#にHashMapはある?結論はDictionary<TKey, TValue>を使う
C#でJavaのHashMapのようなデータ構造を使いたい場合は、基本的にDictionary<TKey, TValue>を使います。
Dictionary<TKey, TValue>は、キーと値のペアを管理するためのコレクションです。キーを指定すると、それに対応する値を高速に取得できます。
たとえば、次のように使います。
C#Dictionary<int, string> users = new Dictionary<int, string>();
users.Add(1, "田中");
users.Add(2, "佐藤");
Console.WriteLine(users[1]); // 田中
この例では、1や2がキー、"田中"や"佐藤"が値です。
JavaのHashMapに慣れている方であれば、C#ではまずDictionaryを覚えると理解しやすいです。
1-1. C#にはJavaのHashMapという名前のクラスはない
C#には、JavaのようにHashMapという名前の標準クラスはありません。
Javaでは次のように書きます。
JavaHashMap<Integer, String> users = new HashMap<>();
一方、C#では次のように書きます。
C#Dictionary<int, string> users = new Dictionary<int, string>();
つまり、JavaのHashMapに相当するものをC#で使いたい場合は、HashMapではなくDictionaryを使うと考えてください。
C#でHashMapと検索している方の多くは、実際にはDictionary<TKey, TValue>の使い方を知りたいケースがほとんどです。
1-2. HashMapの役割はDictionary<TKey, TValue>が担う
JavaのHashMapは、キーと値をペアで保存し、キーを使って値を高速に取り出すためのクラスです。
C#のDictionary<TKey, TValue>も同じような目的で使われます。
C#Dictionary<string, int> scores = new Dictionary<string, int>();
scores.Add("Alice", 90);
scores.Add("Bob", 75);
Console.WriteLine(scores["Alice"]); // 90
このように、名前をキーにして点数を取り出せます。
Dictionary<TKey, TValue>のTKeyはキーの型、TValueは値の型を表します。
C#Dictionary<string, int>
この場合は、キーがstring型、値がint型という意味です。
1-3. C#のDictionaryはキーと値を管理する連想配列
Dictionaryは、よく「連想配列」とも呼ばれます。
配列やListでは、通常はインデックス番号を使って値を取り出します。
C#List<string> names = new List<string>();
names.Add("田中");
names.Add("佐藤");
Console.WriteLine(names[0]); // 田中
一方、Dictionaryでは、数値のインデックスではなく、任意のキーを使って値を取り出します。
C#Dictionary<string, string> capitals = new Dictionary<string, string>();
capitals.Add("Japan", "Tokyo");
capitals.Add("USA", "Washington");
Console.WriteLine(capitals["Japan"]); // Tokyo
このように、「キーを指定して値を取り出す」データ管理に向いています。
1-4. JavaのHashMap経験者がC#でまず覚えるべき対応関係
JavaのHashMapを使ったことがある方は、次の対応関係を覚えるとC#のDictionaryを理解しやすくなります。
| Java | C# |
|---|---|
HashMap<K, V> | Dictionary<TKey, TValue> |
put(key, value) | Add(key, value)またはdictionary[key] = value |
get(key) | dictionary[key]またはTryGetValue |
containsKey(key) | ContainsKey(key) |
remove(key) | Remove(key) |
size() | Count |
たとえば、Javaの次のコードは、
Javamap.put("apple", 100);
int price = map.get("apple");
C#では次のように書けます。
C#dictionary.Add("apple", 100);
int price = dictionary["apple"];
または、追加と更新をまとめて次のように書くこともできます。
C#dictionary["apple"] = 100;
2. C#のDictionaryとは?HashMapのように使える基本の仕組み
C#のDictionaryは、キーと値を対応づけて保存するコレクションです。
たとえば、「社員番号から社員名を取得する」「商品コードから価格を取得する」「設定名から設定値を取得する」といった処理に向いています。
C#Dictionary<string, string> settings = new Dictionary<string, string>();
settings.Add("Theme", "Dark");
settings.Add("Language", "Japanese");
Console.WriteLine(settings["Theme"]); // Dark
Dictionaryを理解するポイントは、キーが一意であること、キーを使って高速に値を検索できることです。
2-1. Dictionaryはキーと値のペアを保存するコレクション
Dictionaryは、1つのキーに対して1つの値を保存します。
C#Dictionary<int, string> employees = new Dictionary<int, string>();
employees.Add(1001, "山田");
employees.Add(1002, "鈴木");
employees.Add(1003, "高橋");
この例では、社員番号がキー、社員名が値です。
キーを指定すると、対応する値を取得できます。
C#Console.WriteLine(employees[1002]); // 鈴木
このように、何かのIDやコードをもとにデータを取り出したい場合に便利です。
2-2. キーは重複できず、値は重複できる
Dictionaryでは、キーは重複できません。
C#Dictionary<int, string> users = new Dictionary<int, string>();
users.Add(1, "田中");
users.Add(1, "佐藤"); // エラー
同じキーをAddで追加しようとすると、例外が発生します。
一方、値は重複しても問題ありません。
C#Dictionary<int, string> users = new Dictionary<int, string>();
users.Add(1, "田中");
users.Add(2, "田中");
この場合、キーは1と2で異なるため、値が同じ"田中"でも問題ありません。
2-3. ハッシュテーブルによって高速に検索できる
Dictionaryは内部的にハッシュテーブルを利用して、キーから値を高速に検索します。
通常、配列やListで特定の値を探す場合、先頭から順番に探す必要があります。
C#List<string> names = new List<string> { "田中", "佐藤", "鈴木" };
bool exists = names.Contains("鈴木");
一方、Dictionaryではキーを使って直接値を探せるため、大量のデータを扱う場合でも効率的です。
C#Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "田中" },
{ 2, "佐藤" },
{ 3, "鈴木" }
};
Console.WriteLine(users[3]); // 鈴木
ただし、常にどんなケースでも最速というわけではありません。データ量が少ない場合や、順番に処理するだけの場合は、Listや配列の方がシンプルに書けることもあります。
2-4. Dictionaryを使うために必要な名前空間
Dictionaryを使うには、通常は次の名前空間を利用します。
C#using System.Collections.Generic;
C#ファイルの先頭に次のように書きます。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices.Add("banana", 80);
Console.WriteLine(prices["apple"]);
}
}
最近のC#プロジェクトでは、テンプレートによって暗黙的に名前空間が利用できる場合もありますが、初心者のうちはusing System.Collections.Generic;を覚えておくとよいでしょう。
2-5. Dictionaryが向いているケースと向いていないケース
Dictionaryが向いているのは、キーを使って値をすばやく取り出したいケースです。
たとえば、次のような場面です。
| 用途 | キー | 値 |
|---|---|---|
| ユーザー管理 | ユーザーID | ユーザー名 |
| 商品管理 | 商品コード | 価格 |
| 設定管理 | 設定名 | 設定値 |
| カウント処理 | 単語 | 出現回数 |
| キャッシュ | ID | 取得済みデータ |
一方で、次のような場合はDictionary以外を検討した方がよいことがあります。
| ケース | 向いている型 |
|---|---|
| 順番を重視して一覧表示したい | List |
| インデックス番号でアクセスしたい | 配列、List |
| キーを並び順で管理したい | SortedDictionary |
| 複数スレッドから安全に更新したい | ConcurrentDictionary |
初心者のうちは、「キーで値を取り出したいならDictionary」と覚えておくとよいです。
3. C# Dictionaryの基本的な使い方
ここからは、C#のDictionaryの基本的な使い方をコード付きで解説します。
Dictionaryでは、主に次の操作を行います。
| 操作 | 使用するメソッド・プロパティ |
|---|---|
| 作成 | new Dictionary<TKey, TValue>() |
| 追加 | Add |
| 取得 | dictionary[key] |
| 更新 | dictionary[key] = value |
| 削除 | Remove |
| 全削除 | Clear |
それぞれ順番に見ていきましょう。
3-1. Dictionaryの宣言と初期化
Dictionaryは、次のように宣言します。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
この例では、キーがstring型、値がint型です。
つまり、文字列のキーに対して数値の値を保存できます。
C#prices.Add("apple", 100);
prices.Add("banana", 80);
C# 9.0以降では、型推論を使って次のように短く書ける場合もあります。
C#Dictionary<string, int> prices = new();
また、初期化子を使うと、作成と同時に値を入れることもできます。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
3-2. Addメソッドで要素を追加する
Dictionaryに要素を追加するには、Addメソッドを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices.Add("banana", 80);
prices.Add("orange", 120);
Addメソッドの第1引数にはキー、第2引数には値を指定します。
C#prices.Add("キー", 値);
たとえば、商品名と価格を管理する場合は、商品名をキー、価格を値にできます。
C#prices.Add("coffee", 300);
ただし、すでに存在するキーをAddしようとすると例外が発生します。
C#prices.Add("apple", 150); // すでにappleがある場合はエラー
同じキーの値を変更したい場合は、Addではなくインデクサを使います。
C#prices["apple"] = 150;
3-3. キーを指定して値を取得する
Dictionaryから値を取得するには、キーを指定します。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices.Add("banana", 80);
int applePrice = prices["apple"];
Console.WriteLine(applePrice); // 100
dictionary[key]の形で、指定したキーに対応する値を取得できます。
ただし、存在しないキーを指定すると例外が発生します。
C#Console.WriteLine(prices["melon"]); // キーが存在しないためエラー
安全に値を取得したい場合は、後述するTryGetValueを使うのがおすすめです。
3-4. インデクサで値を追加・更新する
Dictionaryでは、インデクサを使って値を追加または更新できます。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices["apple"] = 100;
prices["banana"] = 80;
この書き方では、キーが存在しない場合は新しく追加されます。
C#prices["orange"] = 120;
すでにキーが存在する場合は、値が更新されます。
C#prices["apple"] = 150;
Addとの違いは、同じキーが存在してもエラーにならず、値が上書きされる点です。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices["apple"] = 150;
Console.WriteLine(prices["apple"]); // 150
新規追加だけを明確にしたい場合はAdd、追加または更新をしたい場合はインデクサを使うとよいでしょう。
3-5. Removeメソッドで要素を削除する
Dictionaryから要素を削除するには、Removeメソッドを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
prices.Remove("banana");
削除後、bananaのキーと値はDictionaryからなくなります。
C#foreach (var item in prices)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
出力結果は次のようになります。
C#apple: 100
orange: 120
Removeは、削除に成功したかどうかをboolで返します。
C#bool removed = prices.Remove("apple");
Console.WriteLine(removed); // True
存在しないキーを削除しようとした場合は、falseが返ります。
C#bool removed = prices.Remove("melon");
Console.WriteLine(removed); // False
3-6. Clearメソッドで全要素を削除する
Dictionaryのすべての要素を削除するには、Clearメソッドを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
prices.Clear();
Console.WriteLine(prices.Count); // 0
Clearを使うと、すべてのキーと値が削除されます。
一時的にデータを保持して、処理が終わったら空にしたい場合などに使えます。
4. Dictionaryでよく使う検索・判定処理
Dictionaryを使うときは、値の追加や取得だけでなく、「キーが存在するか」「値が存在するか」「安全に取得できるか」といった判定処理もよく使います。
特に、存在しないキーを参照すると例外が発生するため、実務ではContainsKeyやTryGetValueをよく使います。
4-1. ContainsKeyでキーの存在を確認する
キーが存在するか確認するには、ContainsKeyメソッドを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 }
};
if (prices.ContainsKey("apple"))
{
Console.WriteLine(prices["apple"]);
}
ContainsKeyは、指定したキーが存在すればtrue、存在しなければfalseを返します。
C#Console.WriteLine(prices.ContainsKey("apple")); // True
Console.WriteLine(prices.ContainsKey("melon")); // False
存在確認をしてから値を取得することで、例外を防げます。
ただし、値の取得まで行う場合は、TryGetValueの方がよりよく使われます。
4-2. ContainsValueで値の存在を確認する
値が存在するか確認するには、ContainsValueメソッドを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 }
};
bool exists = prices.ContainsValue(100);
Console.WriteLine(exists); // True
ContainsValueは、指定した値が存在すればtrue、存在しなければfalseを返します。
C#Console.WriteLine(prices.ContainsValue(80)); // True
Console.WriteLine(prices.ContainsValue(200)); // False
ただし、Dictionaryはキー検索に向いたコレクションです。値から探す処理は、キー検索ほど効率的ではありません。
値で頻繁に検索したい場合は、設計を見直した方がよいこともあります。
4-3. TryGetValueで安全に値を取得する
Dictionaryから安全に値を取得するには、TryGetValueを使うのがおすすめです。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 }
};
if (prices.TryGetValue("apple", out int price))
{
Console.WriteLine(price);
}
else
{
Console.WriteLine("指定したキーは存在しません。");
}
TryGetValueは、キーが存在する場合はtrueを返し、値をout変数に代入します。
キーが存在しない場合はfalseを返します。
C#if (prices.TryGetValue("melon", out int melonPrice))
{
Console.WriteLine(melonPrice);
}
else
{
Console.WriteLine("melonは登録されていません。");
}
存在しないキーを直接参照すると例外が発生しますが、TryGetValueを使えば安全に処理できます。
実務では、単純にdictionary[key]で取得するよりも、TryGetValueを使う場面が多いです。
4-4. Countで要素数を取得する
Dictionaryに登録されている要素数を取得するには、Countプロパティを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
Console.WriteLine(prices.Count); // 3
要素が空かどうかを確認するときにも使えます。
C#if (prices.Count == 0)
{
Console.WriteLine("商品が登録されていません。");
}
else
{
Console.WriteLine($"{prices.Count}件の商品があります。");
}
4-5. foreachでキーと値をループ処理する
Dictionaryの中身を順番に処理するには、foreachを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
foreach (KeyValuePair<string, int> item in prices)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
KeyValuePair<TKey, TValue>を使うと、キーはitem.Key、値はitem.Valueで取得できます。
varを使って短く書くこともできます。
C#foreach (var item in prices)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
出力例は次の通りです。
C#apple: 100
banana: 80
orange: 120
4-6. KeysとValuesを使ってキー・値だけを取り出す
キーだけを取り出したい場合は、Keysプロパティを使います。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
foreach (string key in prices.Keys)
{
Console.WriteLine(key);
}
出力例は次の通りです。
C#apple
banana
orange
値だけを取り出したい場合は、Valuesプロパティを使います。
C#foreach (int value in prices.Values)
{
Console.WriteLine(value);
}
出力例は次の通りです。
C#100
80
120
キーだけ、値だけをまとめて処理したい場合に便利です。
5. C#のDictionaryとHashMap・Hashtable・Mapの違い
C#でHashMapについて調べていると、DictionaryのほかにHashtable、SortedDictionary、ConcurrentDictionary、Mapなどの言葉を目にすることがあります。
初心者のうちは、「何が違うのか」「どれを使えばいいのか」で迷いやすいです。
基本的には、通常のキーと値の管理にはDictionary<TKey, TValue>を使えば問題ありません。
ここでは、それぞれの違いを整理します。
5-1. DictionaryとJavaのHashMapの違い
C#のDictionaryとJavaのHashMapは、どちらもキーと値をペアで管理するためのコレクションです。
役割はよく似ています。
| 比較項目 | C# Dictionary | Java HashMap |
|---|---|---|
| 主な用途 | キーと値の管理 | キーと値の管理 |
| 型指定 | Dictionary<TKey, TValue> | HashMap<K, V> |
| 追加 | Addまたはインデクサ | put |
| 取得 | インデクサまたはTryGetValue | get |
| キー存在確認 | ContainsKey | containsKey |
| 削除 | Remove | remove |
Javaでは次のように書きます。
JavaHashMap<String, Integer> prices = new HashMap<>();
prices.put("apple", 100);
C#では次のように書きます。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
C#にはputメソッドはありません。追加はAdd、更新や追加はインデクサを使います。
C#prices["apple"] = 150;
また、C#では存在しないキーをインデクサで取得しようとすると例外が発生します。そのため、安全に取得したい場合はTryGetValueを使います。
5-2. DictionaryとHashtableの違い
C#にはHashtableというクラスもあります。
Hashtableもキーと値を管理できますが、現在のC#では基本的にDictionary<TKey, TValue>を使うのがおすすめです。
主な違いは、型安全かどうかです。
Hashtableはキーや値をobjectとして扱います。
C#using System.Collections;
Hashtable table = new Hashtable();
table.Add("apple", 100);
table.Add(1, "banana");
このように、異なる型のキーや値を入れることもできますが、取り出すときにキャストが必要になることがあります。
C#int price = (int)table["apple"];
一方、Dictionaryはキーと値の型を明確に指定できます。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
型が決まっているため、間違った型を入れにくく、コードも読みやすくなります。
初心者は、特別な理由がない限りHashtableではなくDictionaryを使いましょう。
5-3. DictionaryとSortedDictionaryの違い
SortedDictionaryは、キーを並び替えた状態で管理するコレクションです。
C#SortedDictionary<string, int> prices = new SortedDictionary<string, int>();
prices.Add("banana", 80);
prices.Add("apple", 100);
prices.Add("orange", 120);
foreach (var item in prices)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
出力例は次のようになります。
C#apple: 100
banana: 80
orange: 120
Dictionaryはキーの順序を目的にしたコレクションではありません。
キーの順番を重視したい場合は、SortedDictionaryを検討します。
| 比較項目 | Dictionary | SortedDictionary |
|---|---|---|
| 主な用途 | 高速なキー検索 | キー順で管理 |
| 並び順 | 順序目的ではない | キー順に並ぶ |
| 初心者向け | 通常はこちら | 並び替えが必要な場合 |
単純にキーから値を取り出したいだけなら、Dictionaryで十分です。
5-4. DictionaryとConcurrentDictionaryの違い
ConcurrentDictionaryは、複数のスレッドから同時にアクセスされる場面を想定したコレクションです。
通常のDictionaryは、複数スレッドから同時に追加・更新すると問題が起きる可能性があります。
一方、ConcurrentDictionaryはマルチスレッド環境で安全に使いやすいように設計されています。
C#using System.Collections.Concurrent;
ConcurrentDictionary<string, int> counts = new ConcurrentDictionary<string, int>();
counts.TryAdd("apple", 1);
counts.AddOrUpdate("apple", 1, (key, oldValue) => oldValue + 1);
通常のアプリケーションや学習用途では、まずDictionaryを使えば問題ありません。
Webアプリや並列処理などで、複数スレッドから同じコレクションを更新する必要がある場合は、ConcurrentDictionaryを検討します。
5-5. DictionaryとList・配列の違い
DictionaryとListや配列は、データの管理方法が異なります。
Listや配列は、順番に並んだデータを管理するのに向いています。
C#List<string> names = new List<string>
{
"田中",
"佐藤",
"鈴木"
};
Console.WriteLine(names[0]); // 田中
一方、Dictionaryはキーで値を取り出すのに向いています。
C#Dictionary<int, string> users = new Dictionary<int, string>
{
{ 101, "田中" },
{ 102, "佐藤" },
{ 103, "鈴木" }
};
Console.WriteLine(users[102]); // 佐藤
一覧を順番に処理したいならList、IDやコードを使って値を取得したいならDictionaryを使うとよいです。
5-6. 初心者はどれを使えばよいか
初心者は、まず次のように考えるとわかりやすいです。
| やりたいこと | 使う型 |
|---|---|
| キーで値を取得したい | Dictionary<TKey, TValue> |
| 順番にデータを管理したい | List<T> |
| 固定サイズのデータを管理したい | 配列 |
| キー順に並べたい | SortedDictionary<TKey, TValue> |
| 複数スレッドで安全に更新したい | ConcurrentDictionary<TKey, TValue> |
C#でJavaのHashMapのようなことをしたい場合は、まずDictionary<TKey, TValue>を使えば問題ありません。
6. C# Dictionaryの実装例
ここからは、実際によく使うDictionaryの実装例を紹介します。
Dictionaryは、単にサンプルコードで使うだけでなく、実務でもよく登場します。
ユーザー情報、商品情報、カウント処理、設定管理など、さまざまな場面で活用できます。
6-1. ユーザーIDからユーザー名を取得する例
ユーザーIDをキーにして、ユーザー名を取得する例です。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary<int, string> users = new Dictionary<int, string>
{
{ 1, "田中" },
{ 2, "佐藤" },
{ 3, "鈴木" }
};
int userId = 2;
if (users.TryGetValue(userId, out string userName))
{
Console.WriteLine($"ユーザー名: {userName}");
}
else
{
Console.WriteLine("ユーザーが見つかりません。");
}
}
}
出力結果は次の通りです。
C#ユーザー名: 佐藤
IDから名前を取得する処理は、Dictionaryの代表的な使い方です。
存在しないIDが指定される可能性がある場合は、TryGetValueを使うと安全です。
6-2. 商品コードから価格を取得する例
商品コードをキーにして、価格を取得する例です。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
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#価格: 2500円
商品コード、会員番号、注文番号など、一意のコードをキーにしたい場合にDictionaryは便利です。
6-3. 文字列の出現回数をカウントする例
Dictionaryは、文字列や単語の出現回数をカウントする処理にもよく使われます。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
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;
}
}
6-4. 設定値をキーで管理する例
アプリケーションの設定値をDictionaryで管理することもできます。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary<string, string> settings = new Dictionary<string, string>
{
{ "Theme", "Dark" },
{ "Language", "Japanese" },
{ "FontSize", "14" }
};
Console.WriteLine(settings["Theme"]);
Console.WriteLine(settings["Language"]);
}
}
出力結果は次の通りです。
C#Dark
Japanese
設定名をキーにして、対応する設定値を取得できます。
ただし、設定値の数が多い場合や型が複雑な場合は、専用の設定クラスを作った方が管理しやすいこともあります。
6-5. クラスやオブジェクトを値として保存する例
Dictionaryの値には、文字列や数値だけでなく、クラスのインスタンスも保存できます。
C#using System;
using System.Collections.Generic;
class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
class Program
{
static void Main()
{
Dictionary<int, User> users = new Dictionary<int, User>
{
{
1,
new User
{
Id = 1,
Name = "田中",
Email = "tanaka@example.com"
}
},
{
2,
new User
{
Id = 2,
Name = "佐藤",
Email = "sato@example.com"
}
}
};
if (users.TryGetValue(1, out User user))
{
Console.WriteLine(user.Name);
Console.WriteLine(user.Email);
}
}
}
出力結果は次の通りです。
C#田中
tanaka@example.com
実務では、IDをキーにしてオブジェクトを管理するケースがよくあります。
C#Dictionary<int, User>
このように書くことで、ユーザーIDからユーザー情報をすばやく取得できます。
6-6. 実務で使いやすいDictionaryの書き方
実務では、存在しないキーによる例外を避けるため、TryGetValueを使う書き方がよく使われます。
C#if (users.TryGetValue(userId, out User user))
{
Console.WriteLine(user.Name);
}
else
{
Console.WriteLine("ユーザーが存在しません。");
}
また、追加または更新をしたい場合は、インデクサを使うと簡潔です。
C#scores["Alice"] = 90;
ただし、「すでに存在していたら上書きしたくない」という場合は、ContainsKeyで確認するか、TryAddを使います。
C#Dictionary<string, int> scores = new Dictionary<string, int>();
bool added = scores.TryAdd("Alice", 90);
if (!added)
{
Console.WriteLine("すでに登録されています。");
}
実務では、次のような使い分けをすると読みやすくなります。
| 目的 | 書き方 |
|---|---|
| 新規追加だけしたい | AddまたはTryAdd |
| 追加または更新したい | dictionary[key] = value |
| 安全に取得したい | TryGetValue |
| 存在確認だけしたい | ContainsKey |
| 削除したい | Remove |
7. Dictionaryを使うときの注意点とエラー対策
Dictionaryは便利ですが、使い方を間違えると例外が発生することがあります。
特に初心者がつまずきやすいのは、存在しないキーを参照するケースと、同じキーをAddしてしまうケースです。
ここでは、Dictionaryを安全に使うための注意点を解説します。
7-1. 存在しないキーを参照すると例外が発生する
Dictionaryでは、存在しないキーをインデクサで参照すると例外が発生します。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 }
};
Console.WriteLine(prices["banana"]); // エラー
bananaというキーが存在しないため、例外が発生します。
これを防ぐには、ContainsKeyで確認します。
C#if (prices.ContainsKey("banana"))
{
Console.WriteLine(prices["banana"]);
}
else
{
Console.WriteLine("bananaは登録されていません。");
}
よりおすすめなのは、TryGetValueを使う方法です。
C#if (prices.TryGetValue("banana", out int price))
{
Console.WriteLine(price);
}
else
{
Console.WriteLine("bananaは登録されていません。");
}
値を取得する目的なら、TryGetValueの方が自然で安全です。
7-2. 同じキーをAddすると例外が発生する
Addメソッドで同じキーを追加しようとすると例外が発生します。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices.Add("apple", 150); // エラー
同じキーを追加する可能性がある場合は、事前にContainsKeyで確認します。
C#if (!prices.ContainsKey("apple"))
{
prices.Add("apple", 100);
}
または、TryAddを使うこともできます。
C#bool added = prices.TryAdd("apple", 100);
if (!added)
{
Console.WriteLine("すでに登録されています。");
}
すでに存在する場合に値を更新したいなら、インデクサを使います。
C#prices["apple"] = 150;
この書き方なら、キーが存在しない場合は追加、存在する場合は更新になります。
7-3. nullをキーにできるか
Dictionaryでは、通常、キーにnullを指定することはできません。
C#Dictionary<string, int> scores = new Dictionary<string, int>();
scores.Add(null, 100); // エラー
キーは値を検索するための重要な情報なので、nullを使わない設計にするのが基本です。
一方、値については、型によってはnullを入れられます。
C#Dictionary<int, string?> users = new Dictionary<int, string?>();
users.Add(1, null);
ただし、nullを値として許可すると、後続の処理でnullチェックが必要になります。
キーには一意で明確な値を使うようにしましょう。
7-4. キーに独自クラスを使うときの注意点
Dictionaryのキーには、intやstringだけでなく、独自クラスを使うこともできます。
ただし、独自クラスをキーにする場合は、等価比較の扱いに注意が必要です。
たとえば、次のようなクラスがあるとします。
C#class ProductKey
{
public string Code { get; set; }
}
このクラスをキーにすると、同じCodeを持っていても、別のインスタンスなら別のキーとして扱われることがあります。
C#Dictionary<ProductKey, string> products = new Dictionary<ProductKey, string>();
var key1 = new ProductKey { Code = "A001" };
var key2 = new ProductKey { Code = "A001" };
products.Add(key1, "商品A");
Console.WriteLine(products.ContainsKey(key2)); // Falseになる可能性がある
独自クラスをキーとして正しく扱いたい場合は、EqualsとGetHashCodeを適切に実装する必要があります。
初心者のうちは、キーにはint、string、Guidなどの扱いやすい型を使うのがおすすめです。
7-5. 順番が保証されるとは限らない点に注意する
Dictionaryは、キーを使って値を高速に取得するためのコレクションです。
そのため、データの順番を目的にして使うべきではありません。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "banana", 80 },
{ "apple", 100 },
{ "orange", 120 }
};
foreach (var item in prices)
{
Console.WriteLine(item.Key);
}
現在の実行環境では追加順に見えることがあっても、Dictionaryを「順番を保証するためのコレクション」として扱うのは避けた方が安全です。
順番が重要な場合は、次のような型を検討します。
| 目的 | 使う型 |
|---|---|
| 追加順に管理したい | Listと組み合わせる |
| キー順に管理したい | SortedDictionary |
| 並び替えて表示したい | LINQのOrderBy |
たとえば、キー順に表示したい場合は次のように書けます。
C#foreach (var item in prices.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
この場合は、using System.Linq;が必要です。
7-6. 大量データを扱うときのパフォーマンス注意点
Dictionaryは高速な検索に向いていますが、大量データを扱う場合は注意点もあります。
たとえば、要素数が非常に多い場合は、メモリ使用量が増えます。
また、キーのハッシュ値が偏ると、検索効率が悪くなる可能性があります。
大量のデータを追加することがわかっている場合は、初期容量を指定すると効率がよくなることがあります。
C#Dictionary<int, string> users = new Dictionary<int, string>(10000);
このように書くと、あらかじめある程度の容量を確保できます。
また、キーに独自クラスを使う場合は、GetHashCodeの実装が適切でないとパフォーマンスに影響することがあります。
通常の用途ではあまり意識しすぎる必要はありませんが、大量データや高頻度アクセスを扱う場合は、キーの選び方や初期容量にも注意しましょう。
8. Dictionaryをより便利に使う応用テクニック
基本的な使い方に慣れてきたら、Dictionaryをより便利に使う書き方も覚えておくと役立ちます。
ここでは、初期化子、LINQ、ToDictionary、大文字小文字を区別しないキー、ConcurrentDictionary、戻り値としての利用などを紹介します。
8-1. 初期化子を使って簡潔に書く
Dictionaryは、初期化子を使うと作成と同時に値を設定できます。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
また、インデクサ形式の初期化もできます。
C#Dictionary<string, int> prices = new Dictionary<string, int>
{
["apple"] = 100,
["banana"] = 80,
["orange"] = 120
};
この書き方は、キーと値の対応が見やすいというメリットがあります。
初期データが決まっている場合は、Addを何行も書くよりも初期化子を使うと読みやすくなります。
8-2. LINQと組み合わせて検索・変換する
DictionaryはLINQと組み合わせて使うこともできます。
たとえば、価格が100円以上の商品だけを取得する場合は、次のように書けます。
C#using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
Dictionary<string, int> prices = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
var expensiveItems = prices.Where(x => x.Value >= 100);
foreach (var item in expensiveItems)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
}
}
出力結果は次の通りです。
C#apple: 100
orange: 120
キー順に並べたい場合は、OrderByを使います。
C#var sortedItems = prices.OrderBy(x => x.Key);
値の大きい順に並べたい場合は、OrderByDescendingを使います。
C#var sortedByPrice = prices.OrderByDescending(x => x.Value);
8-3. ToDictionaryでListからDictionaryを作成する
ListからDictionaryを作成したい場合は、LINQのToDictionaryが便利です。
C#using System;
using System.Collections.Generic;
using System.Linq;
class User
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
List<User> users = new List<User>
{
new User { Id = 1, Name = "田中" },
new User { Id = 2, Name = "佐藤" },
new User { Id = 3, Name = "鈴木" }
};
Dictionary<int, User> userDictionary = users.ToDictionary(user => user.Id);
Console.WriteLine(userDictionary[2].Name); // 佐藤
}
}
この例では、UserのIdをキーにして、Userオブジェクトを値にしたDictionaryを作成しています。
値に特定のプロパティだけを使いたい場合は、次のように書けます。
C#Dictionary<int, string> userNames = users.ToDictionary(
user => user.Id,
user => user.Name
);
この場合、キーはId、値はNameになります。
ただし、ToDictionaryでキーが重複すると例外が発生するため、元のリストに重複キーがないか注意しましょう。
8-4. キーの大文字・小文字を区別しないDictionaryを作る
通常、Dictionary<string, TValue>では、文字列キーの大文字・小文字は区別されます。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices["Apple"] = 100;
Console.WriteLine(prices.ContainsKey("apple")); // False
大文字・小文字を区別したくない場合は、StringComparer.OrdinalIgnoreCaseを指定します。
C#Dictionary<string, int> prices = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
prices["Apple"] = 100;
Console.WriteLine(prices.ContainsKey("apple")); // True
Console.WriteLine(prices["APPLE"]); // 100
設定名、コマンド名、ヘッダー名など、大文字・小文字を区別したくないキーを扱う場合に便利です。
C#Dictionary<string, string> settings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Theme", "Dark" },
{ "Language", "Japanese" }
};
Console.WriteLine(settings["theme"]); // Dark
8-5. ConcurrentDictionaryでマルチスレッド対応する
複数のスレッドから同じ辞書を更新する場合は、ConcurrentDictionaryを使うことがあります。
C#using System;
using System.Collections.Concurrent;
class Program
{
static void Main()
{
ConcurrentDictionary<string, int> counts = new ConcurrentDictionary<string, int>();
counts.TryAdd("apple", 1);
counts.AddOrUpdate(
"apple",
1,
(key, oldValue) => oldValue + 1
);
Console.WriteLine(counts["apple"]); // 2
}
}
AddOrUpdateは、キーが存在しない場合は追加し、存在する場合は更新します。
通常のDictionaryは、複数スレッドから同時に書き換える用途には向いていません。
マルチスレッド処理、並列処理、キャッシュ管理などで同時更新が発生する場合は、ConcurrentDictionaryを検討しましょう。
8-6. Dictionaryをメソッドの戻り値として使う
Dictionaryは、メソッドの戻り値として使うこともできます。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary<string, int> prices = GetPrices();
Console.WriteLine(prices["apple"]);
}
static Dictionary<string, int> GetPrices()
{
return new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 },
{ "orange", 120 }
};
}
}
このように、キーと値のセットをまとめて返したい場合に便利です。
ただし、戻り値が複雑になりすぎる場合は、専用のクラスを作った方が読みやすくなることもあります。
たとえば、商品情報に価格、在庫数、商品名など複数の情報がある場合は、次のようなクラスを作るとよいです。
C#class Product
{
public string Code { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public int Stock { get; set; }
}
そのうえで、商品コードをキーにして管理します。
C#Dictionary<string, Product> products = new Dictionary<string, Product>();
9. C# HashMap・Dictionaryに関するよくある質問
最後に、C#のHashMapやDictionaryについて、初心者が疑問に思いやすいポイントをQ&A形式で整理します。
9-1. C#でHashMapをimportする必要はある?
C#にはJavaのHashMapという名前のクラスはないため、HashMapをimportする必要はありません。
C#でHashMapのような機能を使いたい場合は、Dictionary<TKey, TValue>を使います。
必要な名前空間は次の通りです。
C#using System.Collections.Generic;
使用例は次のようになります。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
JavaのHashMapに慣れている場合は、C#ではDictionaryが対応するものだと考えると理解しやすいです。
9-2. Dictionaryの検索速度はどれくらい速い?
Dictionaryは、キーを使った検索が高速なコレクションです。
内部的にハッシュテーブルを利用しているため、通常はキーから値を効率よく取得できます。
C#int price = prices["apple"];
ただし、実際の速度はデータ量、キーの型、ハッシュ値の分布、実行環境などによって変わります。
小さなデータであればListでも十分なことがありますが、大量のデータからキーで検索する場合はDictionaryが有効です。
たとえば、ユーザーIDからユーザー情報を探すような処理では、Listを毎回検索するよりも、IDをキーにしたDictionaryの方が効率的に書けます。
9-3. Dictionaryのキーにはどんな型を使える?
Dictionaryのキーには、さまざまな型を使えます。
よく使われるキーの型は次の通りです。
| キーの型 | 用途例 |
|---|---|
int | ID、番号 |
string | 名前、コード、設定名 |
Guid | 一意識別子 |
enum | 種別、状態 |
| 独自クラス | 複合的なキー |
初心者におすすめなのは、intやstringをキーにする使い方です。
C#Dictionary<int, string> users = new Dictionary<int, string>();
Dictionary<string, int> prices = new Dictionary<string, int>();
独自クラスをキーにする場合は、等価比較やハッシュコードの扱いに注意が必要です。
9-4. Dictionaryは順序を保持する?
Dictionaryは、順序を目的に使うコレクションではありません。
そのため、「追加した順番で必ず処理したい」「キーの順番で表示したい」といった用途では注意が必要です。
順番が重要な場合は、目的に応じて別の方法を検討します。
| 目的 | 方法 |
|---|---|
| キー順に表示したい | OrderByまたはSortedDictionary |
| 値順に表示したい | LINQのOrderBy |
| 追加順を厳密に管理したい | Listと組み合わせる |
たとえば、キー順に表示する場合は次のように書けます。
C#foreach (var item in prices.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
9-5. DictionaryとHashtableはどちらを使うべき?
基本的には、Dictionary<TKey, TValue>を使うべきです。
Hashtableは古くからあるコレクションで、キーや値をobjectとして扱います。
そのため、型安全性が低く、取り出すときにキャストが必要になることがあります。
C#Hashtable table = new Hashtable();
table.Add("apple", 100);
int price = (int)table["apple"];
一方、Dictionaryはキーと値の型を明確に指定できます。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
int price = prices["apple"];
コードの安全性や読みやすさを考えると、初心者も実務でもDictionaryを使うのが一般的です。
9-6. JavaのHashMapからC#のDictionaryへ書き換えるには?
JavaのHashMapをC#のDictionaryに書き換える場合は、主にメソッド名の違いを覚えるとスムーズです。
Javaのコード例です。
JavaHashMap<String, Integer> prices = new HashMap<>();
prices.put("apple", 100);
prices.put("banana", 80);
int applePrice = prices.get("apple");
C#では次のように書けます。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices.Add("banana", 80);
int applePrice = prices["apple"];
また、Javaのputのように「追加または更新」をしたい場合は、C#ではインデクサを使います。
C#prices["apple"] = 100;
prices["banana"] = 80;
存在確認は、JavaではcontainsKey、C#ではContainsKeyです。
C#if (prices.ContainsKey("apple"))
{
Console.WriteLine(prices["apple"]);
}
安全に取得したい場合は、C#ではTryGetValueを使うのがおすすめです。
C#if (prices.TryGetValue("apple", out int price))
{
Console.WriteLine(price);
}
JavaのHashMapからC#に移行する場合は、「HashMapではなくDictionary」「putではなくAddまたはインデクサ」「getではなくインデクサまたはTryGetValue」と覚えておきましょう。
まとめ
C#には、JavaのHashMapという名前のクラスはありません。
C#でHashMapのようにキーと値を管理したい場合は、基本的にDictionary<TKey, TValue>を使います。
Dictionaryは、キーと値のペアを保存し、キーを指定して値を高速に取得できる便利なコレクションです。
基本的な使い方は次の通りです。
C#Dictionary<string, int> prices = new Dictionary<string, int>();
prices.Add("apple", 100);
prices["banana"] = 80;
Console.WriteLine(prices["apple"]);
ただし、存在しないキーを参照すると例外が発生するため、安全に値を取得したい場合はTryGetValueを使うのがおすすめです。
C#if (prices.TryGetValue("apple", out int price))
{
Console.WriteLine(price);
}
初心者は、まず次のポイントを押さえておきましょう。
| ポイント | 内容 |
|---|---|
| C#にHashMapはあるか | HashMapという名前の標準クラスはない |
| 代わりに使うもの | Dictionary<TKey, TValue> |
| 主な用途 | キーと値の管理 |
| キーの重複 | できない |
| 値の重複 | できる |
| 安全な取得方法 | TryGetValue |
| 通常使う名前空間 | System.Collections.Generic |
JavaのHashMap経験者であれば、C#ではDictionary<TKey, TValue>が対応するものだと理解すれば問題ありません。
ユーザーIDからユーザー名を取得する、商品コードから価格を取得する、文字列の出現回数をカウントするなど、Dictionaryは実務でもよく使われます。
C#でキーと値を効率よく管理したいときは、まずDictionary<TKey, TValue>を使えるようになりましょう。

