C#のMapとは?Dictionaryとの違い・使い方・変換処理を初心者向けに徹底解説

はじめに

C#を学んでいると、「csharp map」「C# Map」「MapとDictionaryの違い」といったキーワードで調べる場面があります。

しかし、C#にはJavaのMapのような名前の標準クラスは基本的に存在しません。C#で「Map」と言われる場合、多くは次のいずれかを意味します。

1つ目は、キーと値のペアでデータを管理するDictionary<TKey, TValue>です。
2つ目は、Listや配列の各要素を別の形に変換する処理です。C#ではLINQのSelectを使います。
3つ目は、DTOやEntityなど、あるオブジェクトを別のオブジェクトへ変換する「オブジェクトマッピング」です。

つまり、C#におけるMapは文脈によって意味が変わります。

この記事では、C#初心者向けに「Mapとは何か」「Dictionaryとの違い」「LINQのSelectを使った変換処理」「オブジェクト同士のマッピング」まで、実務で使える形でわかりやすく解説します。

1. C#のMapとは?まず結論から理解しよう

1-1. C#に「Map」という標準クラスは存在するのか

まず結論から言うと、C#の標準ライブラリには、JavaのようなMapという名前の代表的なコレクションクラスはありません。

Javaでは次のようにMapを使います。

Java
Map<String, Integer> scores = new HashMap<>();

一方、C#では同じような目的で次のようにDictionaryを使います。

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

そのため、C#で「Mapを使いたい」と思った場合、多くのケースではDictionary<TKey, TValue>を使えば問題ありません。

ただし、「Map」という言葉は「データを対応づける」という意味でも使われます。そのため、C#では単にDictionaryだけでなく、LINQのSelectや、DTO変換などのオブジェクトマッピングを指すこともあります。

1-2. C#でMapと呼ばれるものは主にDictionaryを指す

C#でMapと呼ばれるものの代表がDictionary<TKey, TValue>です。

Dictionaryは、キーと値をセットで管理するコレクションです。

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

userNames.Add(1, "田中");
userNames.Add(2, "佐藤");

Console.WriteLine(userNames[1]); // 田中

この例では、1というキーに対して"田中"という値を対応づけています。

このような「キーから値を取り出す」仕組みは、JavaのMap、JavaScriptのMap、Pythonのdictと似ています。

そのため、C#で「Mapのようなもの」と言われた場合は、まずDictionaryをイメージすると理解しやすいです。

1-3. Java・JavaScript・PythonのMapとの呼び方の違い

プログラミング言語によって、Mapに相当する機能の名前は異なります。

JavaではMapインターフェースを使い、具体的な実装としてHashMapTreeMapがあります。

Java
Map<String, String> map = new HashMap<>();

JavaScriptにはMapオブジェクトがあります。

JavaScript
const map = new Map();
map.set("name", "Taro");

Pythonではdictを使います。

Python
user = {
"name": "Taro",
"age": 20
}

C#では、これらに近い役割を持つのがDictionary<TKey, TValue>です。

C#
var user = new Dictionary<string, object>
{
{ "name", "Taro" },
{ "age", 20 }
};

つまり、言語ごとに名前は違いますが、「キーと値を対応づける」という考え方は共通しています。

1-4. 「Map」が意味する3つのケース:連想配列・変換処理・オブジェクトマッピング

C#で「Map」という言葉が出てきたときは、主に次の3つの意味があります。

1つ目は、連想配列としてのMapです。これはDictionary<TKey, TValue>を指すことが多いです。

C#
var prices = new Dictionary<string, int>
{
{ "apple", 120 },
{ "banana", 80 }
};

2つ目は、変換処理としてのMapです。たとえば、数値のListを2倍に変換するような処理です。C#ではLINQのSelectを使います。

C#
var numbers = new List<int> { 1, 2, 3 };
var doubled = numbers.Select(x => x * 2).ToList();

3つ目は、オブジェクトマッピングです。たとえば、UserEntityUserDtoに変換するような処理です。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.Name
};

このように、「csharp map」と検索したときに出てくる情報は、必ずしもDictionaryだけではありません。文脈に応じて意味を判断することが大切です。

2. MapとDictionaryの違いを初心者向けに解説

2-1. Dictionary<TKey, TValue>とは何か

Dictionary<TKey, TValue>は、キーと値のペアを管理するC#のコレクションです。

TKeyはキーの型、TValueは値の型を表します。

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

この場合、キーはstring型、値はint型です。

C#
scores.Add("Taro", 90);
scores.Add("Hanako", 85);

これにより、"Taro"というキーに90"Hanako"というキーに85を対応づけられます。

値を取り出すときは、キーを指定します。

C#
Console.WriteLine(scores["Taro"]); // 90

配列やListでは、基本的にインデックス番号を使って要素にアクセスします。一方、Dictionaryでは意味のあるキーを使って値にアクセスできます。

2-2. MapとDictionaryは何が同じで何が違うのか

MapとDictionaryは、どちらも「キーと値を対応づける」という点では同じです。

たとえば、ユーザーIDからユーザー名を取得する場合、次のような構造になります。

C#
var users = new Dictionary<int, string>
{
{ 1, "田中" },
{ 2, "佐藤" },
{ 3, "鈴木" }
};

このような使い方は、JavaのMapとほぼ同じ考え方です。

違いは、C#では標準的な名前がMapではなくDictionaryである点です。

JavaではMapという抽象的な型を使い、HashMapなどの実装クラスを選びます。C#では多くの場合、Dictionary<TKey, TValue>をそのまま使います。

つまり、C#で「Map」と言われたら、まずは「Dictionaryのことだな」と考えるとよいでしょう。

2-3. キーと値のペアでデータを管理する仕組み

Dictionaryでは、1つのキーに対して1つの値を対応づけます。

C#
var countries = new Dictionary<string, string>
{
{ "JP", "日本" },
{ "US", "アメリカ" },
{ "FR", "フランス" }
};

この例では、国コードをキーにして、国名を値として管理しています。

C#
Console.WriteLine(countries["JP"]); // 日本

キーは重複できません。同じキーを複数登録すると、どの値を取り出せばよいかわからなくなるためです。

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

map.Add("apple", 100);
map.Add("apple", 120); // エラー

一方、値は重複しても問題ありません。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 80 },
{ "Hanako", 80 }
};

このように、Dictionaryでは「キーは一意」「値は重複可能」と覚えておきましょう。

2-4. 配列・List・Dictionaryの違い

C#には、データをまとめて扱うための代表的なコレクションとして、配列、List、Dictionaryがあります。

配列は、要素数がある程度決まっているデータを扱うときに使います。

C#
int[] numbers = { 10, 20, 30 };
Console.WriteLine(numbers[0]); // 10

Listは、要素を後から追加・削除したいときに使います。

C#
var names = new List<string>();
names.Add("田中");
names.Add("佐藤");

Console.WriteLine(names[0]); // 田中

Dictionaryは、キーから値をすばやく取り出したいときに使います。

C#
var userNames = new Dictionary<int, string>
{
{ 1001, "田中" },
{ 1002, "佐藤" }
};

Console.WriteLine(userNames[1001]); // 田中

違いを簡単にまとめると、配列とListは「順番」で管理し、Dictionaryは「キー」で管理します。

たとえば、単に名前を順番に並べるだけならListで十分です。しかし、ユーザーIDから名前を取得したい場合はDictionaryが向いています。

2-5. Dictionaryを使うべき場面・使わないほうがよい場面

Dictionaryを使うべき場面は、キーを使って値を取得したい場合です。

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

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

商品コードから価格を取得したい場合、Dictionaryは非常に便利です。

C#
int price = productPrices["A001"];
Console.WriteLine(price); // 1200

また、設定値を管理する場合にも使われます。

C#
var settings = new Dictionary<string, string>
{
{ "Theme", "Dark" },
{ "Language", "ja" }
};

一方、Dictionaryを使わないほうがよい場面もあります。

順番が重要なデータを扱う場合は、Listのほうが自然です。

C#
var steps = new List<string>
{
"ログイン",
"商品を選択",
"購入"
};

また、同じキーを複数持たせたい場合、Dictionaryは向いていません。たとえば、カテゴリごとに複数の商品を管理したい場合は、値をListにするなどの工夫が必要です。

C#
var productsByCategory = new Dictionary<string, List<string>>
{
{ "Food", new List<string> { "Apple", "Banana" } },
{ "Drink", new List<string> { "Water", "Tea" } }
};

Dictionaryは便利ですが、すべてのデータ管理に向いているわけではありません。キーで検索したいかどうかを基準に選ぶとよいでしょう。

3. C# Dictionaryの基本的な使い方

3-1. Dictionaryの宣言・初期化方法

Dictionaryを使うには、まず型を指定して宣言します。

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

C#ではvarを使って次のように書くこともできます。

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

初期値を入れて作成する場合は、次のように書きます。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 },
{ "Jiro", 78 }
};

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

C#
var scores = new Dictionary<string, int>
{
["Taro"] = 90,
["Hanako"] = 85,
["Jiro"] = 78
};

どちらの書き方でもDictionaryを初期化できます。初心者のうちは、{ キー, 値 }の形式から覚えると理解しやすいです。

3-2. 要素を追加する:Addメソッドとインデクサ

Dictionaryに要素を追加する方法は主に2つあります。

1つ目はAddメソッドです。

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

scores.Add("Taro", 90);
scores.Add("Hanako", 85);

2つ目はインデクサを使う方法です。

C#
scores["Jiro"] = 78;

どちらも要素を追加できますが、動きに違いがあります。

Addは、同じキーがすでに存在するとエラーになります。

C#
scores.Add("Taro", 95); // エラー

一方、インデクサで代入すると、キーが存在しない場合は追加され、すでに存在する場合は値が更新されます。

C#
scores["Taro"] = 95; // 既存の値を更新

新しいキーだけを追加したいときはAdd、追加と更新の両方を許可したいときはインデクサを使うとよいでしょう。

3-3. 値を取得する:キー指定とTryGetValue

Dictionaryから値を取得するには、キーを指定します。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 }
};

int taroScore = scores["Taro"];
Console.WriteLine(taroScore); // 90

ただし、存在しないキーを指定するとエラーになります。

C#
Console.WriteLine(scores["Jiro"]); // エラー

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

C#
if (scores.TryGetValue("Jiro", out int score))
{
Console.WriteLine(score);
}
else
{
Console.WriteLine("指定されたキーは存在しません");
}

実務では、存在しないキーの可能性がある場合、TryGetValueを使うことが多いです。

特に外部データ、APIレスポンス、ユーザー入力などを扱う場合は、キーが必ず存在するとは限りません。安全なコードを書くためにも、TryGetValueを覚えておきましょう。

3-4. 値を更新する方法

Dictionaryの値を更新するには、インデクサを使います。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 }
};

scores["Taro"] = 95;

Console.WriteLine(scores["Taro"]); // 95

このように、キーを指定して新しい値を代入すれば更新できます。

ただし、存在しないキーに代入した場合は、新しい要素として追加されます。

C#
scores["Jiro"] = 78; // 新規追加

「存在する場合だけ更新したい」という場合は、ContainsKeyで確認してから更新します。

C#
if (scores.ContainsKey("Taro"))
{
scores["Taro"] = 100;
}

また、値が存在しない場合は追加し、存在する場合は更新するという処理では、インデクサが便利です。

C#
scores["Taro"] = 100;

3-5. 要素を削除する方法

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

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 }
};

scores.Remove("Taro");

削除後に確認すると、"Taro"のキーは存在しなくなります。

C#
Console.WriteLine(scores.ContainsKey("Taro")); // False

Removeは、指定したキーが存在しない場合でも例外にはなりません。

C#
scores.Remove("Jiro"); // エラーにはならない

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

C#
bool removed = scores.Remove("Hanako");

if (removed)
{
Console.WriteLine("削除しました");
}
else
{
Console.WriteLine("キーが見つかりませんでした");
}

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

C#
scores.Clear();

3-6. キーや値の存在を確認する方法

キーが存在するかどうかを確認するには、ContainsKeyを使います。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 }
};

if (scores.ContainsKey("Taro"))
{
Console.WriteLine("Taroのデータがあります");
}

値が存在するかどうかを確認するには、ContainsValueを使います。

C#
if (scores.ContainsValue(90))
{
Console.WriteLine("90点の人がいます");
}

ただし、Dictionaryはキー検索に向いた構造です。ContainsKeyはよく使いますが、ContainsValueは値を順番に探すため、データ量が多い場合は注意が必要です。

値を取得する目的でContainsKeyとインデクサを組み合わせる場合、次のように書けます。

C#
if (scores.ContainsKey("Taro"))
{
Console.WriteLine(scores["Taro"]);
}

ただし、値を取得するだけならTryGetValueのほうが効率的で安全です。

C#
if (scores.TryGetValue("Taro", out int score))
{
Console.WriteLine(score);
}

3-7. foreachでキーと値を取り出す方法

Dictionaryの全要素を取り出すには、foreachを使います。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 },
{ "Jiro", 78 }
};

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

item.Keyでキー、item.Valueで値を取得できます。

C#では分解代入を使って、次のように書くこともできます。

C#
foreach (var (name, score) in scores)
{
Console.WriteLine($"{name}: {score}");
}

キーだけをループしたい場合は、Keysを使います。

C#
foreach (var key in scores.Keys)
{
Console.WriteLine(key);
}

値だけをループしたい場合は、Valuesを使います。

C#
foreach (var value in scores.Values)
{
Console.WriteLine(value);
}

Dictionaryを使うときは、キーと値の両方を扱う場面が多いため、foreachでの取り出し方は必ず覚えておきましょう。

4. Dictionaryを使うときの注意点とよくあるエラー

4-1. 同じキーを追加するとエラーになる理由

Dictionaryでは、同じキーを複数登録できません。

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

scores.Add("Taro", 90);
scores.Add("Taro", 95); // エラー

これは、キーが一意である必要があるためです。

もし同じキーが複数存在すると、次のコードを書いたときに、どちらの値を返せばよいかわからなくなります。

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

そのため、Addで既存のキーを追加しようとすると例外が発生します。

すでにキーが存在する可能性がある場合は、ContainsKeyで確認します。

C#
if (!scores.ContainsKey("Taro"))
{
scores.Add("Taro", 90);
}

または、追加・更新の両方を許可するならインデクサを使います。

C#
scores["Taro"] = 95;

4-2. 存在しないキーを参照したときのエラー

Dictionaryで存在しないキーをインデクサで参照すると、エラーになります。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 }
};

Console.WriteLine(scores["Hanako"]); // エラー

これは、"Hanako"というキーがDictionary内に存在しないためです。

このエラーを防ぐには、TryGetValueを使います。

C#
if (scores.TryGetValue("Hanako", out int score))
{
Console.WriteLine(score);
}
else
{
Console.WriteLine("キーが見つかりません");
}

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

C#
if (scores.ContainsKey("Hanako"))
{
Console.WriteLine(scores["Hanako"]);
}

ただし、値の取得が目的なら、TryGetValueのほうがよく使われます。

4-3. nullをキーや値に使うときの注意点

Dictionaryでは、キーにnullを使うことはできません。

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

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

キーは値を探すための重要な情報です。そのため、nullのように比較や検索が曖昧になる値はキーとして使えません。

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

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

users.Add(1, "田中");
users.Add(2, null);

ただし、値にnullが入る可能性がある場合は、取り出した後の処理に注意が必要です。

C#
if (users.TryGetValue(2, out string? name))
{
if (name != null)
{
Console.WriteLine(name.Length);
}
}

C#ではnullable reference typesを有効にしている場合、stringstring?の違いも重要になります。

nullを扱う可能性があるなら、型定義の段階でstring?のように明示しておくと、コードの意図がわかりやすくなります。

4-4. キーの型を選ぶときの考え方

Dictionaryのキーには、検索に使いやすく、一意に識別できる型を選ぶのが基本です。

よく使われるキーの型は次のようなものです。

C#
Dictionary<int, string> usersById = new Dictionary<int, string>();
Dictionary<string, int> pricesByCode = new Dictionary<string, int>();
Dictionary<Guid, string> itemsByGuid = new Dictionary<Guid, string>();

ユーザーID、商品コード、GUIDなどは、キーとして使いやすい代表例です。

一方、変更されやすい値をキーにするのは避けたほうがよいです。

たとえば、ユーザー名は後から変更される可能性があります。

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

ユーザー名が変わると、キーとしての意味も変わってしまいます。

そのため、実務ではユーザーIDのように変更されにくい値をキーにすることが多いです。

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

キーの型を選ぶときは、「一意か」「変更されにくいか」「検索に使いやすいか」を基準にしましょう。

4-5. Dictionaryの順序は保証されるのか

Dictionaryを使うときに注意したいのが、要素の順序です。

Dictionaryは、基本的に「キーから値を高速に取得する」ためのコレクションです。Listのように順番を管理することが主目的ではありません。

C#
var map = new Dictionary<string, int>
{
{ "A", 1 },
{ "B", 2 },
{ "C", 3 }
};

foreachで取り出したときに、登録した順番で出てくるように見える場合もあります。しかし、順序に依存したコードを書くのは避けたほうが安全です。

順番が重要な場合は、Listを使うか、並び替えを明示します。

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

キー順で管理したい場合は、SortedDictionaryを検討できます。

C#
var sorted = new SortedDictionary<string, int>
{
{ "B", 2 },
{ "A", 1 },
{ "C", 3 }
};

Dictionaryは「順番」ではなく「キー検索」に向いたものと覚えておきましょう。

4-6. 初心者が混乱しやすいAddと代入の違い

初心者がよく混乱するのが、Addとインデクサ代入の違いです。

Addは新しいキーを追加するためのメソッドです。

C#
scores.Add("Taro", 90);

同じキーがすでに存在するとエラーになります。

C#
scores.Add("Taro", 95); // エラー

一方、インデクサ代入は、キーがなければ追加し、キーがあれば更新します。

C#
scores["Taro"] = 95;

つまり、次のように使い分けます。

新規追加だけを許可したい場合はAdd

C#
scores.Add("Hanako", 85);

追加でも更新でもよい場合はインデクサ。

C#
scores["Hanako"] = 88;

既存のキーに対する上書きを防ぎたい場合は、Addを使うほうが安全です。逆に、設定値の更新やキャッシュの更新では、インデクサのほうが便利です。

5. C#でMap処理を行う方法:LINQのSelectを理解する

5-1. 変換処理としてのMapとは何か

プログラミングで「map」と言う場合、コレクションの各要素を別の形に変換する処理を指すことがあります。

たとえば、次のようなListがあるとします。

C#
var numbers = new List<int> { 1, 2, 3 };

これをすべて2倍に変換したい場合、結果は次のようになります。

C#
{ 2, 4, 6 }

このように、元の各要素に処理を適用して、新しいコレクションを作るのが変換処理としてのMapです。

JavaScriptでは次のようにmapを使います。

JavaScript
const doubled = numbers.map(x => x * 2);

C#では、このmapに相当する処理をSelectで書きます。

C#
var doubled = numbers.Select(x => x * 2).ToList();

C#で「配列をmapしたい」「Listをmapしたい」と言われた場合、多くはSelectを使うという意味になります。

5-2. C#ではmapではなくSelectを使う

C#のLINQでは、変換処理にSelectを使います。

C#
var numbers = new List<int> { 1, 2, 3 };

var doubled = numbers.Select(x => x * 2).ToList();

Selectの中に書くx => x * 2はラムダ式です。

xはListの各要素を表します。
x * 2は変換後の値です。

つまり、次のような意味になります。

C#
1  2
2 4
3 6

Selectは元のListを直接変更するわけではありません。変換結果として新しいシーケンスを作ります。

C#
var numbers = new List<int> { 1, 2, 3 };
var doubled = numbers.Select(x => x * 2).ToList();

Console.WriteLine(string.Join(",", numbers)); // 1,2,3
Console.WriteLine(string.Join(",", doubled)); // 2,4,6

元データを壊さずに変換できる点が、LINQの便利なところです。

5-3. Listの各要素を別の形に変換する基本例

Listの各要素を変換する基本例を見てみましょう。

C#
var names = new List<string> { "tanaka", "sato", "suzuki" };

var upperNames = names.Select(name => name.ToUpper()).ToList();

foreach (var name in upperNames)
{
Console.WriteLine(name);
}

出力結果は次のようになります。

C#
TANAKA
SATO
SUZUKI

数値を文字列に変換することもできます。

C#
var numbers = new List<int> { 1, 2, 3 };

var labels = numbers.Select(x => $"No.{x}").ToList();

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

C#
No.1
No.2
No.3

オブジェクトを別の形に変換することもできます。

C#
var users = new List<User>
{
new User { Id = 1, Name = "田中" },
new User { Id = 2, Name = "佐藤" }
};

var userNames = users.Select(user => user.Name).ToList();

このように、SelectはC#のMap処理で最もよく使うメソッドです。

5-4. Selectでオブジェクトの一部だけを取り出す方法

Selectは、オブジェクトの一部だけを取り出すときにも使えます。

たとえば、次のようなクラスがあるとします。

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

ユーザー一覧から名前だけを取り出す場合は、次のように書きます。

C#
var users = new List<User>
{
new User { Id = 1, Name = "田中", Age = 25 },
new User { Id = 2, Name = "佐藤", Age = 30 }
};

var names = users.Select(user => user.Name).ToList();

結果はList<string>になります。

C#
田中
佐藤

複数のプロパティを取り出して、新しい匿名型に変換することもできます。

C#
var summaries = users.Select(user => new
{
user.Id,
user.Name
}).ToList();

この場合、Ageは含まれません。

DTOに変換する場合もSelectが使えます。

C#
var userDtos = users.Select(user => new UserDto
{
Id = user.Id,
Name = user.Name
}).ToList();

このように、Selectは「必要なデータだけを取り出す」「別の型に変換する」といった場面で非常によく使われます。

5-5. SelectとWhereの違い

SelectWhereはどちらもLINQでよく使いますが、役割が違います。

Selectは変換です。

C#
var numbers = new List<int> { 1, 2, 3 };

var doubled = numbers.Select(x => x * 2).ToList();

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

C#
2, 4, 6

一方、Whereは絞り込みです。

C#
var numbers = new List<int> { 1, 2, 3, 4, 5 };

var evenNumbers = numbers.Where(x => x % 2 == 0).ToList();

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

C#
2, 4

つまり、Selectは「形を変える」、Whereは「条件に合うものだけ残す」と覚えるとわかりやすいです。

両方を組み合わせることもできます。

C#
var numbers = new List<int> { 1, 2, 3, 4, 5 };

var result = numbers
.Where(x => x % 2 == 0)
.Select(x => x * 10)
.ToList();

この場合、まず偶数だけを取り出し、その後10倍に変換します。

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

C#
20, 40

5-6. SelectManyとの違い

SelectManyは、複数のコレクションを平らにする場合に使います。

たとえば、次のようなデータがあるとします。

C#
var groups = new List<List<int>>
{
new List<int> { 1, 2 },
new List<int> { 3, 4 },
new List<int> { 5 }
};

Selectを使うと、Listの中にListがある構造のままです。

C#
var selected = groups.Select(group => group).ToList();

一方、SelectManyを使うと、すべての要素を1つのListにまとめられます。

C#
var flattened = groups.SelectMany(group => group).ToList();

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

C#
1, 2, 3, 4, 5

実務では、ユーザーごとの注文一覧をまとめたい場合などに使います。

C#
var allOrders = users.SelectMany(user => user.Orders).ToList();

Selectは1要素を1つの結果に変換します。
SelectManyは、1要素から複数の結果を取り出し、それをまとめます。

この違いを理解しておくと、LINQのMap処理をより柔軟に使えるようになります。

6. List・配列・Dictionaryの変換処理

6-1. ListからDictionaryに変換する方法

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

たとえば、ユーザー一覧をユーザーIDで検索できるようにしたい場合を考えます。

C#
var users = new List<User>
{
new User { Id = 1, Name = "田中" },
new User { Id = 2, Name = "佐藤" }
};

var userMap = users.ToDictionary(user => user.Id);

この場合、キーはId、値はUserオブジェクトになります。

C#
var user = userMap[1];
Console.WriteLine(user.Name); // 田中

値を名前だけにしたい場合は、第2引数を指定します。

C#
var userNameMap = users.ToDictionary(
user => user.Id,
user => user.Name
);

これで、Dictionary<int, string>が作られます。

C#
Console.WriteLine(userNameMap[1]); // 田中

ListをDictionaryに変換すると、キーで高速に検索できるようになります。頻繁に検索するデータは、Dictionary化すると便利です。

6-2. 配列からDictionaryに変換する方法

配列からDictionaryに変換する場合も、ToDictionaryを使います。

C#
var products = new[]
{
new Product { Code = "A001", Name = "Apple", Price = 120 },
new Product { Code = "B002", Name = "Banana", Price = 80 }
};

var productMap = products.ToDictionary(product => product.Code);

これで、商品コードをキーにしたDictionaryが作られます。

C#
var product = productMap["A001"];
Console.WriteLine(product.Name); // Apple

値を価格だけにする場合は、次のように書きます。

C#
var priceMap = products.ToDictionary(
product => product.Code,
product => product.Price
);

結果はDictionary<string, int>になります。

C#
Console.WriteLine(priceMap["A001"]); // 120

配列でもListでも、LINQを使えば同じようにDictionaryへ変換できます。

6-3. DictionaryからListに変換する方法

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

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 }
};

var list = scores.ToList();

この場合、listList<KeyValuePair<string, int>>になります。

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

キーと値を別のオブジェクトに変換してListにすることもできます。

C#
var scoreList = scores.Select(x => new ScoreDto
{
Name = x.Key,
Score = x.Value
}).ToList();

クラスは次のように定義できます。

C#
public class ScoreDto
{
public string Name { get; set; } = "";
public int Score { get; set; }
}

Dictionaryは検索に向いていますが、画面表示や一覧処理ではListのほうが扱いやすい場合があります。そのようなときに、DictionaryからListへ変換します。

6-4. Dictionaryのキーだけ・値だけを取り出す方法

Dictionaryからキーだけを取り出すには、Keysを使います。

C#
var scores = new Dictionary<string, int>
{
{ "Taro", 90 },
{ "Hanako", 85 }
};

var names = scores.Keys.ToList();

この場合、namesにはキーだけが入ります。

C#
Taro
Hanako

値だけを取り出すには、Valuesを使います。

C#
var scoreValues = scores.Values.ToList();

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

C#
90
85

Selectを使って取り出すこともできます。

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

キーや値をさらに加工したい場合は、Selectのほうが便利です。

C#
var labels = scores.Select(x => $"{x.Key}さん: {x.Value}点").ToList();

6-5. ToDictionaryを使うときの重複キーエラー対策

ToDictionaryを使うときに注意したいのが、キーの重複です。

次の例を見てみましょう。

C#
var users = new List<User>
{
new User { Id = 1, Name = "田中" },
new User { Id = 1, Name = "佐藤" }
};

var userMap = users.ToDictionary(user => user.Id); // エラー

Idが重複しているため、Dictionaryに変換できません。

対策としては、事前に重複を除外する方法があります。

C#
var userMap = users
.GroupBy(user => user.Id)
.ToDictionary(
group => group.Key,
group => group.First()
);

この例では、同じIDのユーザーが複数いる場合、最初の1件だけを採用します。

また、重複しているデータをListとして保持したい場合は、値をListにします。

C#
var userMap = users
.GroupBy(user => user.Id)
.ToDictionary(
group => group.Key,
group => group.ToList()
);

この場合、型はDictionary<int, List<User>>になります。

重複キーがあり得るデータでは、単純にToDictionaryを使うのではなく、「重複したときにどうするか」を決めてから変換しましょう。

6-6. Select・Where・ToDictionaryを組み合わせた実践例

実務では、SelectWhereToDictionaryを組み合わせることがよくあります。

たとえば、有効なユーザーだけを取り出して、IDと名前のDictionaryを作る例です。

C#
var users = new List<User>
{
new User { Id = 1, Name = "田中", IsActive = true },
new User { Id = 2, Name = "佐藤", IsActive = false },
new User { Id = 3, Name = "鈴木", IsActive = true }
};

var activeUserMap = users
.Where(user => user.IsActive)
.ToDictionary(
user => user.Id,
user => user.Name
);

この処理では、まずWhereで有効なユーザーだけを残します。

C#
user.IsActive == true

その後、ToDictionaryでIDをキー、名前を値に変換します。

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

C#
1 -> 田中
3 -> 鈴木

さらに、名前を加工したい場合はSelectを挟むこともできます。

C#
var activeUserMap = users
.Where(user => user.IsActive)
.Select(user => new
{
user.Id,
DisplayName = $"{user.Name}さん"
})
.ToDictionary(
user => user.Id,
user => user.DisplayName
);

LINQを使うと、絞り込み、変換、Dictionary化を流れるように書けます。

7. オブジェクト同士をMapする方法

7-1. オブジェクトマッピングとは何か

オブジェクトマッピングとは、ある型のオブジェクトを別の型のオブジェクトへ変換することです。

たとえば、データベース用のUserEntityを、APIレスポンス用のUserDtoに変換するような処理です。

C#
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string PasswordHash { get; set; } = "";
}

public class UserDto
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

UserEntityにはPasswordHashがありますが、APIレスポンスには含めたくないとします。

この場合、UserDtoに必要な情報だけを詰め替えます。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.Name
};

このような変換処理も、C#では「Mapする」と呼ばれることがあります。

7-2. DTO・Entity・ViewModelを変換する場面

実務では、DTO、Entity、ViewModelという言葉がよく出てきます。

Entityは、主にデータベースのテーブル構造に近いオブジェクトです。

C#
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public string PasswordHash { get; set; } = "";
}

DTOは、データの受け渡しに使うオブジェクトです。

C#
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

ViewModelは、画面表示に必要な情報をまとめたオブジェクトです。

C#
public class UserViewModel
{
public string DisplayName { get; set; } = "";
}

Entityをそのまま画面やAPIに返すと、不要な情報や機密情報まで含まれる可能性があります。

そのため、実務では次のような変換を行います。

C#
UserEntity -> UserDto
UserEntity -> UserViewModel
Dto -> Entity

これがオブジェクトマッピングです。

7-3. 手動でオブジェクトをマッピングする方法

最も基本的なオブジェクトマッピングは、手動でプロパティを代入する方法です。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.Name
};

List全体を変換する場合は、Selectを使います。

C#
var userDtos = users.Select(user => new UserDto
{
Id = user.Id,
Name = user.Name
}).ToList();

手動マッピングのメリットは、処理内容がわかりやすいことです。

どのプロパティをどのプロパティに入れているのかが明確です。

C#
var viewModel = new UserViewModel
{
DisplayName = $"{entity.Name}さん"
};

加工処理や条件分岐も簡単に書けます。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = string.IsNullOrWhiteSpace(entity.Name)
? "名前未設定"
: entity.Name
};

初心者のうちは、まず手動マッピングを理解することが大切です。AutoMapperなどのライブラリを使う場合でも、裏側で何が起きているかを理解しやすくなります。

7-4. AutoMapperを使うべきケース

AutoMapperは、オブジェクト同士のマッピングを自動化するためのライブラリです。

たとえば、同じ名前のプロパティが多い場合、手動で毎回代入を書くのは面倒です。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.Name,
Email = entity.Email
};

このような変換が大量にある場合、AutoMapperを使うとコード量を減らせます。

イメージとしては、次のような設定を書きます。

C#
CreateMap<UserEntity, UserDto>();

そして、変換時に次のように使います。

C#
var dto = mapper.Map<UserDto>(entity);

AutoMapperを使うべきケースは、次のような場合です。

同じようなマッピング処理が大量にある場合。
プロパティ名がほとんど一致している場合。
DTOやViewModelへの変換処理を統一したい場合。
チーム内でAutoMapperの運用ルールが決まっている場合。

特に大規模なWebアプリケーションでは、EntityからDTOへの変換が多くなるため、AutoMapperが役立つことがあります。

7-5. AutoMapperを使わないほうがよいケース

AutoMapperは便利ですが、常に使うべきとは限りません。

小さなプロジェクトや、変換処理が少ない場合は、手動マッピングのほうがわかりやすいです。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.Name
};

また、複雑な条件分岐や業務ロジックを含む変換では、AutoMapperに任せすぎると処理が追いづらくなる場合があります。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.IsDeleted ? "退会済みユーザー" : entity.Name
};

このような処理は、手動で書いたほうが意図が明確です。

AutoMapperを使わないほうがよいケースは、次のような場合です。

変換処理が少ない場合。
変換ロジックが複雑な場合。
デバッグしやすさを優先したい場合。
チームメンバーがAutoMapperに慣れていない場合。
暗黙的な変換を避けたい場合。

便利なライブラリほど、使いどころを見極めることが大切です。

7-6. Dictionaryからクラスへ変換する考え方

Dictionaryからクラスへ変換する場面もあります。

たとえば、次のようなDictionaryがあるとします。

C#
var dict = new Dictionary<string, string>
{
{ "Name", "田中" },
{ "Email", "tanaka@example.com" }
};

これをUserDtoに変換する場合、キーを指定して値を取り出します。

C#
var dto = new UserDto
{
Name = dict["Name"],
Email = dict["Email"]
};

ただし、キーが存在しない可能性がある場合は、TryGetValueを使うほうが安全です。

C#
dict.TryGetValue("Name", out string? name);
dict.TryGetValue("Email", out string? email);

var dto = new UserDto
{
Name = name ?? "",
Email = email ?? ""
};

数値に変換する場合は、型変換も必要です。

C#
var dict = new Dictionary<string, string>
{
{ "Id", "1" },
{ "Name", "田中" }
};

int.TryParse(dict.GetValueOrDefault("Id"), out int id);

var dto = new UserDto
{
Id = id,
Name = dict.GetValueOrDefault("Name") ?? ""
};

Dictionaryは柔軟ですが、キー名の間違いや型変換ミスが起きやすいです。

可能であれば、早い段階でクラスに変換して、型安全に扱うことをおすすめします。

8. 実務でよく使うC# Map・Dictionaryのサンプル

8-1. ユーザーIDからユーザー名を取得する例

実務でよくあるのが、ユーザーIDからユーザー名を取得する処理です。

C#
var userNames = new Dictionary<int, string>
{
{ 1, "田中" },
{ 2, "佐藤" },
{ 3, "鈴木" }
};

ユーザーIDを指定して名前を取得します。

C#
int userId = 2;

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

TryGetValueを使うことで、存在しないユーザーIDにも安全に対応できます。

ListからDictionaryを作る場合は、次のように書けます。

C#
var users = new List<User>
{
new User { Id = 1, Name = "田中" },
new User { Id = 2, Name = "佐藤" }
};

var userNameMap = users.ToDictionary(
user => user.Id,
user => user.Name
);

頻繁にID検索を行う場合、Listを毎回検索するよりもDictionaryに変換しておくと扱いやすくなります。

8-2. 商品コードから価格を取得する例

商品コードから価格を取得する場合にもDictionaryが便利です。

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

商品コードを指定して価格を取得します。

C#
string productCode = "B002";

if (priceMap.TryGetValue(productCode, out int price))
{
Console.WriteLine($"{price}円");
}
else
{
Console.WriteLine("商品コードが見つかりません");
}

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

C#
priceMap["A001"] = 1300;

新商品を追加する場合もインデクサで書けます。

C#
priceMap["D004"] = 1500;

ただし、意図せず上書きしたくない場合はAddを使いましょう。

C#
priceMap.Add("E005", 3000);

8-3. 設定値をDictionaryで管理する例

アプリケーションの簡単な設定値をDictionaryで管理することもあります。

C#
var settings = new Dictionary<string, string>
{
{ "Theme", "Dark" },
{ "Language", "ja" },
{ "PageSize", "20" }
};

設定値を取得する例です。

C#
string theme = settings.GetValueOrDefault("Theme") ?? "Light";
Console.WriteLine(theme);

数値として使いたい場合は変換します。

C#
int pageSize = 10;

if (int.TryParse(settings.GetValueOrDefault("PageSize"), out int parsedPageSize))
{
pageSize = parsedPageSize;
}

設定値は外部ファイルや環境変数から読み込まれることも多く、存在しないキーや不正な値が混ざる可能性があります。

そのため、TryGetValueTryParseを使って安全に処理することが重要です。

8-4. CSVやJSONデータをDictionaryに変換する例

CSVやJSONのような外部データをDictionaryに変換することもあります。

たとえば、CSVの1行を次のようにDictionaryで表現できます。

C#
var row = new Dictionary<string, string>
{
{ "Id", "1" },
{ "Name", "田中" },
{ "Age", "25" }
};

これをクラスに変換する場合は、次のようにします。

C#
int.TryParse(row.GetValueOrDefault("Id"), out int id);
int.TryParse(row.GetValueOrDefault("Age"), out int age);

var user = new User
{
Id = id,
Name = row.GetValueOrDefault("Name") ?? "",
Age = age
};

JSONを扱う場合も、構造が固定されていないデータではDictionaryが使われることがあります。

C#
var jsonMap = new Dictionary<string, object>
{
{ "name", "田中" },
{ "age", 25 },
{ "isActive", true }
};

ただし、JSONの構造が決まっている場合は、Dictionaryよりもクラスにデシリアライズするほうが安全です。

C#
public class UserResponse
{
public string Name { get; set; } = "";
public int Age { get; set; }
public bool IsActive { get; set; }
}

Dictionaryは柔軟ですが、型安全性はクラスのほうが高いです。データ構造が決まっているならクラス、動的なデータならDictionaryと使い分けましょう。

8-5. APIレスポンスをDTOへマッピングする例

APIレスポンスをDTOへマッピングする処理も、実務ではよく使います。

たとえば、外部APIから取得したレスポンス用のクラスがあるとします。

C#
public class UserApiResponse
{
public int user_id { get; set; }
public string user_name { get; set; } = "";
public string mail_address { get; set; } = "";
}

アプリ内部では、次のようなDTOを使いたいとします。

C#
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}

この場合、プロパティ名が違うため、手動でマッピングします。

C#
var dto = new UserDto
{
Id = response.user_id,
Name = response.user_name,
Email = response.mail_address
};

Listで受け取った場合は、Selectで変換します。

C#
var dtos = responses.Select(response => new UserDto
{
Id = response.user_id,
Name = response.user_name,
Email = response.mail_address
}).ToList();

外部APIのデータ構造をそのままアプリ全体に広げると、変更に弱くなります。

DTOへマッピングしておくことで、外部APIの仕様変更の影響を一部に閉じ込めやすくなります。

8-6. UnityでDictionaryを使う場合の例

UnityでもDictionaryはよく使われます。

たとえば、アイテムIDからアイテム情報を取得する場合です。

C#
public class Item
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Price { get; set; }
}

アイテム一覧をDictionaryに変換します。

C#
var items = new List<Item>
{
new Item { Id = 1, Name = "Potion", Price = 50 },
new Item { Id = 2, Name = "Sword", Price = 300 }
};

var itemMap = items.ToDictionary(item => item.Id);

アイテムIDから情報を取得します。

C#
int itemId = 1;

if (itemMap.TryGetValue(itemId, out Item? item))
{
Debug.Log(item.Name);
}

Unityでは、敵ID、アイテムID、ステージID、サウンド名などからデータを取得する場面が多くあります。

C#
var soundMap = new Dictionary<string, AudioClip>();

ただし、UnityのInspectorでは通常のDictionaryをそのまま表示・編集しにくい場合があります。そのため、Inspectorで編集したいデータはListで持ち、実行時にDictionaryへ変換する設計がよく使われます。

C#
[SerializeField]
private List<Item> items = new List<Item>();

private Dictionary<int, Item> itemMap = new Dictionary<int, Item>();

private void Awake()
{
itemMap = items.ToDictionary(item => item.Id);
}

このように、編集用はList、検索用はDictionaryという使い分けが実務的です。

9. C# Map・Dictionaryに関するよくある質問

9-1. C#にJavaのMapと同じものはある?

C#にJavaのMapと同じ名前の標準クラスはありません。

JavaのMapに近いものは、C#ではDictionary<TKey, TValue>です。

Javaでは次のように書きます。

Java
Map<String, Integer> map = new HashMap<>();

C#では次のように書きます。

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

基本的な考え方は同じです。キーと値をペアで管理し、キーを使って値を取得します。

C#
map["apple"] = 100;
Console.WriteLine(map["apple"]);

そのため、Java経験者がC#でMapを使いたい場合は、まずDictionary<TKey, TValue>を覚えるとよいでしょう。

9-2. DictionaryとHashtableの違いは?

Dictionary<TKey, TValue>Hashtableは、どちらもキーと値を管理できます。

ただし、現在のC#では基本的にDictionary<TKey, TValue>を使うのが一般的です。

Hashtableは古いコレクションで、キーや値をobjectとして扱います。

C#
var table = new Hashtable();

table.Add("apple", 100);
table.Add(1, "one");

いろいろな型を入れられる反面、取り出すときにキャストが必要になり、型安全ではありません。

C#
int price = (int)table["apple"];

一方、Dictionaryはキーと値の型を明確に指定できます。

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

prices.Add("apple", 100);

型が決まっているため、間違った型のデータを入れにくくなります。

C#
// prices.Add("banana", "安い"); // コンパイルエラー

特別な理由がなければ、HashtableではなくDictionary<TKey, TValue>を使いましょう。

9-3. DictionaryとSortedDictionaryの違いは?

DictionarySortedDictionaryの違いは、キーの順序で管理されるかどうかです。

Dictionaryは、キーで値を高速に検索するためのコレクションです。順序を目的としたものではありません。

C#
var map = new Dictionary<string, int>
{
{ "B", 2 },
{ "A", 1 },
{ "C", 3 }
};

一方、SortedDictionaryはキーの順序で並びます。

C#
var sortedMap = new SortedDictionary<string, int>
{
{ "B", 2 },
{ "A", 1 },
{ "C", 3 }
};

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

出力はキー順になります。

C#
A: 1
B: 2
C: 3

キー順で一覧表示したい場合は、SortedDictionaryが便利です。

ただし、単純に検索したいだけならDictionaryで十分なことが多いです。

9-4. DictionaryとConcurrentDictionaryの違いは?

ConcurrentDictionary<TKey, TValue>は、複数のスレッドから同時にアクセスされることを想定したDictionaryです。

通常のDictionaryは、複数スレッドから同時に書き込みを行うと問題が起きる可能性があります。

たとえば、複数の処理が同時に同じDictionaryを更新する場合です。

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

このような場合は、ConcurrentDictionaryを検討します。

C#
var map = new ConcurrentDictionary<string, int>();

map.TryAdd("apple", 100);
map["banana"] = 80;

値を安全に追加・更新するためのメソッドも用意されています。

C#
map.AddOrUpdate(
"apple",
100,
(key, oldValue) => oldValue + 10
);

通常のアプリケーションコードではDictionaryで十分なことが多いですが、並列処理やマルチスレッド処理ではConcurrentDictionaryが役立ちます。

9-5. Dictionaryの検索速度は速い?

Dictionaryは、キーを使った検索が非常に得意です。

Listで特定のIDを探す場合、通常は先頭から順番に探します。

C#
var user = users.FirstOrDefault(user => user.Id == targetId);

データ件数が少なければ問題ありませんが、件数が増えると検索回数も増えやすくなります。

一方、Dictionaryではキーを指定して値を取得できます。

C#
var user = userMap[targetId];

頻繁に検索するデータであれば、ListよりDictionaryのほうが向いていることが多いです。

ただし、Dictionaryを作るためのコストやメモリ使用量もあります。そのため、数件程度の小さなデータや、検索回数が少ない場合はListのままでも問題ありません。

使い分けの目安は、次のように考えるとわかりやすいです。

一覧を順番に処理するだけならList。
キーで何度も検索するならDictionary。
キー順で並べたいならSortedDictionary。
複数スレッドで更新するならConcurrentDictionary。

9-6. Map処理にはSelectとAutoMapperのどちらを使うべき?

C#でMap処理を行う場合、まずはSelectによる手動マッピングを理解することが大切です。

Listの要素を別の型に変換するなら、Selectで十分なケースが多いです。

C#
var userDtos = users.Select(user => new UserDto
{
Id = user.Id,
Name = user.Name
}).ToList();

この書き方は、変換内容が明確で、初心者にも読みやすいです。

一方、AutoMapperは、同じようなオブジェクト変換が大量にある場合に便利です。

C#
var dto = mapper.Map<UserDto>(entity);

ただし、AutoMapperは設定が必要で、変換処理が見えにくくなることもあります。

そのため、次のように使い分けるとよいでしょう。

小規模な変換や、条件分岐がある変換はSelectや手動マッピング。
大量の単純な変換がある場合はAutoMapper。
変換内容を明確に見せたい場合は手動マッピング。
チームでルール化されている場合はAutoMapper。

初心者はまずSelectを使ったMap処理をしっかり覚え、その後必要に応じてAutoMapperを学ぶのがおすすめです。

まとめ

C#で「Map」と言われるものには、主に3つの意味があります。

1つ目は、キーと値を対応づけるDictionary<TKey, TValue>です。JavaのMapやJavaScriptのMapに近い役割を持ちます。

C#
var map = new Dictionary<string, int>
{
{ "apple", 100 },
{ "banana", 80 }
};

2つ目は、Listや配列の各要素を別の形に変換するMap処理です。C#ではmapではなく、LINQのSelectを使います。

C#
var doubled = numbers.Select(x => x * 2).ToList();

3つ目は、DTO、Entity、ViewModelなどのオブジェクト同士を変換するオブジェクトマッピングです。

C#
var dto = new UserDto
{
Id = entity.Id,
Name = entity.Name
};

C#にはMapという名前の代表的な標準クラスはありませんが、文脈に応じてDictionarySelect、手動マッピング、AutoMapperなどを使い分けます。

初心者がまず覚えるべきなのは、次の3つです。

キーと値の管理にはDictionary<TKey, TValue>を使う。
コレクションの変換にはLINQのSelectを使う。
オブジェクト同士の変換は、まず手動マッピングから理解する。

「csharp map」と調べて混乱したときは、それが「連想配列としてのMap」なのか、「変換処理としてのMap」なのか、「オブジェクトマッピング」なのかを切り分けて考えると、C#のMapを正しく理解できます。