C# Listの使い方完全ガイド|初期化・追加・削除・検索・ソートまで初心者向けに解説

はじめに

C#で複数のデータをまとめて扱うときに、特によく使われるのがList<T>です。Listを利用すると、文字列や数値、クラスのオブジェクトなどを格納し、必要に応じて要素を追加・削除・検索・並び替えできます。

配列も複数のデータを扱える機能ですが、作成後に要素数を変更しにくいという特徴があります。一方、Listは要素数を柔軟に増減できるため、登録件数が変化するデータの管理に適しています。

この記事では、C# Listの初期化から追加、削除、検索、ソート、LINQとの組み合わせまで、初心者にもわかりやすく解説します。

1. C#のListとは?初心者向けに基本をわかりやすく解説

1-1. Listとは何か

List<T>は、同じ型のデータを順番に格納するためのコレクションです。

たとえば、複数の商品名を管理する場合は、次のようにList<string>を使用します。

List<string> products = new List<string>();

products.Add("パソコン");products.Add("キーボード");products.Add("マウス");

この例では、productsというListに3つの文字列を格納しています。

List<T>Tには、格納するデータの型を指定します。

List<int> numbers = new List<int>();List<string> names = new List<string>();List<double> prices = new List<double>();

List<int>には整数、List<string>には文字列、List<double>には小数を格納できます。

1-2. 配列との違い

配列とListの大きな違いは、要素数を変更できるかどうかです。

配列は、作成時に要素数を決めるのが基本です。

string[] names = new string[3];

この配列には3つの要素を格納できます。後から4つ目の要素を追加するには、より大きな配列を作り直す必要があります。

一方、Listは必要に応じて要素を追加できます。

List<string> names = new List<string>();

names.Add("田中");names.Add("佐藤");names.Add("鈴木");names.Add("高橋");

要素数があらかじめ決まっていない場合や、実行中にデータが増減する場合は、Listのほうが扱いやすいでしょう。

1-3. Listを使うメリット

C#でListを使う主なメリットは、次のとおりです。

  • 要素を後から自由に追加できる

  • 不要な要素を簡単に削除できる

  • インデックス番号で要素を取得できる

  • 条件に一致する要素を検索できる

  • 並び替えや重複削除を行える

  • LINQと組み合わせて柔軟にデータを処理できる

  • ジェネリックにより型安全にデータを管理できる

Listは、要素数が変化するデータを扱う多くのプログラムで利用されています。

1-4. Listがよく使われる場面

Listは、次のような場面でよく使われます。

  • ユーザー一覧の管理

  • 商品一覧の管理

  • 検索結果の格納

  • データベースから取得したデータの保持

  • CSVファイルから読み込んだデータの管理

  • ゲーム内のアイテムやキャラクターの管理

  • APIから取得したデータの管理

  • 入力された値の一時保存

要素数が固定されておらず、後からデータを追加・削除する可能性がある場合は、Listが有力な選択肢になります。

2. C# Listを使うための準備

2-1. System.Collections.Genericのインポート

List<T>は、System.Collections.Generic名前空間に含まれています。

C#ファイルの先頭に次のusingディレクティブを記述します。

using System.Collections.Generic;

一般的なコンソールアプリケーションでは、次のように記述できます。

using System;using System.Collections.Generic;

class Program{static void Main(){List<string> names = new List<string>();names.Add("田中");

    Console.WriteLine(names<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[0]</span>);}

}

.NETのプロジェクト設定によっては、暗黙的なusingが有効になっており、明示的な記述が不要な場合もあります。ただし、Listが認識されないときはusing System.Collections.Generic;が記述されているか確認してください。

2-2. Listの基本構文

Listを作成する基本構文は次のとおりです。

List<データ型> 変数名 = new List<データ型>();

文字列を格納するListは、次のように作成します。

List<string> names = new List<string>();

整数を格納するListは次のとおりです。

List<int> numbers = new List<int>();

作成直後のListは空です。Addなどのメソッドを使って要素を追加します。

2-3. 型パラメータの意味

List<T>Tは、Listに格納するデータ型を表す型パラメータです。

たとえば、List<string>を作成した場合は、原則として文字列だけを格納できます。

List<string> names = new List<string>();

names.Add("田中");// names.Add(100); // コンパイルエラー

異なる型の値を誤って追加しようとすると、コンパイル時にエラーになります。この仕組みにより、実行前に型の間違いを発見しやすくなります。

2-4. よく使うListの型

C#では、次のような型のListがよく使われます。

List<string> names = new List<string>();List<int> scores = new List<int>();List<double> prices = new List<double>();List<bool> flags = new List<bool>();List<DateTime> dates = new List<DateTime>();

独自に作成したクラスもListに格納できます。

List<User> users = new List<User>();

このように、Listには組み込み型だけでなく、クラスや構造体なども指定できます。

3. C# Listの初期化方法

3-1. 空のListを作成する方法

空のListを作成するには、引数なしのコンストラクターを使用します。

List<string> names = new List<string>();

作成時点で要素数は0です。

Console.WriteLine(names.Count); // 0

データを後から追加する場合に適した初期化方法です。

3-2. 初期値を入れてListを作成する方法

コレクション初期化子を使うと、Listの作成と同時に初期値を設定できます。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

整数のListも同じように初期化できます。

List<int> scores = new List<int>{80,95,72};

最初から格納するデータが決まっている場合は、コレクション初期化子を使うとコードが読みやすくなります。

3-3. varを使ったListの初期化

右辺から型を判断できる場合は、varを使用できます。

var names = new List<string>();

初期値を設定する場合は次のように記述します。

var names = new List<string>{"田中","佐藤","鈴木"};

varを使用しても、変数の型が自由に変化するわけではありません。この例のnamesは、コンパイル時にList<string>として決定されます。

3-4. オブジェクトを格納するListの初期化

独自クラスのオブジェクトをListに格納できます。

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

Userオブジェクトを格納するListは、次のように初期化します。

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

ユーザー、商品、注文など、複数の項目を持つデータをまとめて管理するときに便利です。

3-5. 配列からListを作成する方法

配列をListのコンストラクターに渡すと、配列の内容を持つListを作成できます。

string[] nameArray ={"田中","佐藤","鈴木"};

List<string> names = new List<string>(nameArray);

LINQのToListを使う方法もあります。

using System.Linq;

List<string> names = nameArray.ToList();

どちらの方法でも、配列とは別のListが作成されます。

4. C# Listに要素を追加する方法

4-1. Addで要素を追加する

Listの末尾に1つの要素を追加するには、Addメソッドを使用します。

List<string> names = new List<string>();

names.Add("田中");names.Add("佐藤");names.Add("鈴木");

追加された要素は、呼び出した順番でListの末尾に格納されます。

Console.WriteLine(names[0]); // 田中Console.WriteLine(names[1]); // 佐藤Console.WriteLine(names[2]); // 鈴木

4-2. AddRangeで複数の要素を追加する

複数の要素をまとめて末尾に追加するには、AddRangeメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤"};

names.AddRange(new[] { "鈴木", "高橋" });

別のListを追加することもできます。

List<string> newNames = new List<string>{"伊藤","渡辺"};

names.AddRange(newNames);

複数の要素を追加する場合は、Addを繰り返すよりもAddRangeのほうが意図をわかりやすく表現できます。

4-3. Insertで指定位置に追加する

指定したインデックス位置に1つの要素を追加するには、Insertメソッドを使用します。

List<string> names = new List<string>{"田中","鈴木"};

names.Insert(1, "佐藤");

実行後のListは次の順番になります。

田中佐藤鈴木

もともとインデックス1以降にあった要素は、後ろへ移動します。

4-4. InsertRangeで複数要素を指定位置に追加する

複数の要素を指定位置に追加するには、InsertRangeメソッドを使用します。

List<string> names = new List<string>{"田中","高橋"};

names.InsertRange(1, new[] { "佐藤", "鈴木" });

実行後のListは次の順番になります。

田中佐藤鈴木高橋

4-5. 追加時によくあるエラーと対処法

Listへの追加時には、型の不一致やインデックスの指定ミスに注意が必要です。

たとえば、List<int>に文字列は追加できません。

List<int> numbers = new List<int>();

// numbers.Add("100"); // コンパイルエラーnumbers.Add(100);

文字列を数値に変換したい場合は、int.TryParseなどを使用します。

string input = "100";

if (int.TryParse(input, out int number)){numbers.Add(number);}

Insertで指定できるインデックスは、0以上かつCount以下です。

List<string> names = new List<string>{"田中","佐藤"};

names.Insert(names.Count, "鈴木");

Countを指定すると、末尾への追加になります。Countより大きな値や負の値を指定すると、ArgumentOutOfRangeExceptionが発生します。

5. C# Listの要素を取得・参照する方法

5-1. インデックス番号で要素を取得する

Listの要素は、インデックス番号を使って取得できます。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

Console.WriteLine(names[0]); // 田中Console.WriteLine(names[1]); // 佐藤Console.WriteLine(names[2]); // 鈴木

C#のListは0番目から始まります。最初の要素は[0]、2番目の要素は[1]です。

5-2. Countで要素数を取得する

Listの要素数は、Countプロパティで取得できます。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

Console.WriteLine(names.Count); // 3

最後の要素を取得したい場合は、Count - 1をインデックスに指定します。

string lastName = names[names.Count - 1];

Console.WriteLine(lastName); // 鈴木

ただし、空のListでCount - 1を使用すると不正なインデックスになるため、事前に要素が存在するか確認してください。

if (names.Count > 0){Console.WriteLine(names[names.Count - 1]);}

5-3. foreachでListを順番に処理する

Listの全要素を順番に処理するには、foreach文が便利です。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

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

foreachはインデックスを意識せずに全要素を処理できるため、一覧表示などに適しています。

5-4. for文でListを処理する

インデックス番号が必要な場合は、for文を使用します。

for (int i = 0; i < names.Count; i++){Console.WriteLine($"{i}: {names[i]}");}

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

0: 田中1: 佐藤2: 鈴木

i <= names.Countとすると、最後に存在しないインデックスへアクセスしてしまいます。条件はi < names.Countにします。

5-5. 存在しないインデックスを参照したときのエラー

Listの範囲外のインデックスを参照すると、通常はArgumentOutOfRangeExceptionが発生します。

List<string> names = new List<string>{"田中","佐藤"};

// Console.WriteLine(names[2]); // 例外

要素数が2の場合、有効なインデックスは0と1です。

安全に参照するには、インデックスが範囲内か確認します。

int index = 2;

if (index >= 0 && index < names.Count){Console.WriteLine(names[index]);}else{Console.WriteLine("指定された位置に要素がありません。");}

6. C# Listの要素を変更・更新する方法

6-1. インデックスを指定して値を変更する

インデックスを指定して新しい値を代入すると、Listの要素を変更できます。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

names[1] = "高橋";

Console.WriteLine(names[1]); // 高橋

インデックス1に格納されていた「佐藤」が「高橋」に置き換わります。

6-2. 条件に一致する要素を変更する

条件に一致する要素の位置をFindIndexで取得し、その要素を変更できます。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

int index = names.FindIndex(name => name == "佐藤");

if (index >= 0){names[index] = "高橋";}

FindIndexは、条件に一致する最初の要素のインデックスを返します。見つからない場合は-1を返します。

複数の要素を変更する場合は、for文を使用できます。

List<int> scores = new List<int>{40,80,45,90};

for (int i = 0; i < scores.Count; i++){if (scores[i] < 50){scores[i] = 50;}}

この例では、50未満の値をすべて50に変更しています。

6-3. オブジェクトListのプロパティを更新する

クラスのオブジェクトを格納したListでは、取得したオブジェクトのプロパティを変更できます。

class User{public int Id { get; set; }public string Name { get; set; } = "";}
List<User> users = new List<User>{new User { Id = 1, Name = "田中" },new User { Id = 2, Name = "佐藤" }};

User? user = users.Find(x => x.Id == 2);

if (user != null){user.Name = "鈴木";}

Userがクラスの場合、List内のオブジェクトと取得したuserは同じインスタンスを参照します。そのため、プロパティを変更するとList内のデータにも反映されます。

6-4. List更新時の注意点

値型と参照型では、取得した要素を更新したときの動作が異なります。

intなどの値型は、変数に取り出して変更してもListには反映されません。

List<int> numbers = new List<int> { 10 };

int number = numbers[0];number = 20;

Console.WriteLine(numbers[0]); // 10

List自体を更新するには、インデックスを使って代入します。

numbers[0] = 20;

また、条件に一致する要素が存在するとは限りません。FindIndexの結果や、Findが返した値を確認してから更新してください。

7. C# Listから要素を削除する方法

7-1. Removeで指定した値を削除する

指定した値を削除するには、Removeメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

bool removed = names.Remove("佐藤");

Console.WriteLine(removed); // True

Removeは、指定した値と一致する最初の要素だけを削除します。削除できた場合はtrue、対象が見つからなかった場合はfalseを返します。

bool removed = names.Remove("高橋");

if (!removed){Console.WriteLine("削除対象が見つかりませんでした。");}

7-2. RemoveAtで指定位置の要素を削除する

インデックスを指定して削除するには、RemoveAtメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

names.RemoveAt(1);

実行後のListは次のようになります。

田中鈴木

存在しないインデックスを指定すると例外が発生するため、範囲を確認します。

int index = 1;

if (index >= 0 && index < names.Count){names.RemoveAt(index);}

7-3. RemoveRangeで範囲指定して削除する

連続する複数の要素を削除するには、RemoveRangeメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木","高橋","伊藤"};

names.RemoveRange(1, 3);

第1引数は削除を開始するインデックス、第2引数は削除する要素数です。

この例では、インデックス1から3件を削除するため、「佐藤」「鈴木」「高橋」が削除されます。

7-4. RemoveAllで条件に一致する要素を削除する

条件に一致するすべての要素を削除するには、RemoveAllメソッドを使用します。

List<int> numbers = new List<int>{10,20,30,40,50};

int removedCount = numbers.RemoveAll(number => number >= 30);

Console.WriteLine(removedCount); // 3

実行後のListには、10と20だけが残ります。

RemoveAllの戻り値は、削除された要素数です。

7-5. ClearでListを空にする

Listの全要素を削除するには、Clearメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

names.Clear();

Console.WriteLine(names.Count); // 0

Clearを実行してもList変数自体がnullになるわけではありません。要素数が0のListとして引き続き使用できます。

7-6. foreach中に削除するときの注意点

foreachでListを処理している途中に、同じListから要素を削除すると例外が発生します。

List<int> numbers = new List<int>{1,2,3,4};

// 実行時エラーになる// foreach (int number in numbers)// {// if (number % 2 == 0)// {// numbers.Remove(number);// }// }

条件に一致する要素を削除する場合は、RemoveAllを使う方法が簡単です。

numbers.RemoveAll(number => number % 2 == 0);

インデックスを使う場合は、末尾から逆順に削除します。

for (int i = numbers.Count - 1; i >= 0; i--){if (numbers[i] % 2 == 0){numbers.RemoveAt(i);}}

後ろから削除すれば、まだ処理していない要素のインデックスがずれません。

8. C# Listで要素を検索する方法

8-1. Containsで要素の存在を確認する

指定した値がListに含まれているか確認するには、Containsメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

bool exists = names.Contains("佐藤");

Console.WriteLine(exists); // True

Containsは、要素が存在する場合にtrue、存在しない場合にfalseを返します。

文字列の大文字と小文字を無視して検索したい場合は、LINQのAnyなどを使用します。

using System;using System.Linq;

List<string> codes = new List<string>{"ABC","DEF"};

bool exists = codes.Any(code => string.Equals(code, "abc", StringComparison.OrdinalIgnoreCase));

8-2. IndexOfで要素の位置を取得する

指定した要素のインデックスを取得するには、IndexOfメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

int index = names.IndexOf("佐藤");

Console.WriteLine(index); // 1

対象が見つからない場合は-1を返します。

int index = names.IndexOf("高橋");

if (index == -1){Console.WriteLine("見つかりませんでした。");}

同じ値が複数存在する場合は、最初に見つかった要素のインデックスが返されます。

8-3. Findで条件に一致する要素を取得する

条件に一致する最初の要素を取得するには、Findメソッドを使用します。

List<int> numbers = new List<int>{10,25,40,55};

int result = numbers.Find(number => number >= 30);

Console.WriteLine(result); // 40

参照型のListでは、見つからない場合にnullが返る可能性があります。

User? user = users.Find(x => x.Id == 10);

if (user != null){Console.WriteLine(user.Name);}

値型の場合は、見つからなければその型の既定値が返ります。たとえばintでは0が返るため、0が正規のデータとして含まれる場合は、ExistsFindIndexを併用すると判定しやすくなります。

8-4. FindAllで条件に一致する複数要素を取得する

条件に一致するすべての要素を取得するには、FindAllメソッドを使用します。

List<int> numbers = new List<int>{10,20,30,40,50};

List<int> results = numbers.FindAll(number => number >= 30);

resultsには、30、40、50が格納されます。

元のListは変更されず、検索結果を持つ新しいListが返されます。

8-5. Existsで条件に一致する要素があるか確認する

条件に一致する要素が1件以上あるか確認するには、Existsメソッドを使用します。

List<int> scores = new List<int>{45,70,90};

bool hasPassed = scores.Exists(score => score >= 60);

Console.WriteLine(hasPassed); // True

要素自体ではなく、存在するかどうかだけを知りたい場合に適しています。

8-6. LINQのWhereやFirstOrDefaultを使った検索

LINQを使用すると、Listに対して柔軟な条件検索を行えます。

using System.Linq;

条件に一致する複数の要素を取得するには、Whereを使用します。

List<int> numbers = new List<int>{10,20,30,40,50};

List<int> results = numbers.Where(number => number >= 30).ToList();

条件に一致する最初の要素を取得するには、FirstOrDefaultを使用します。

int result = numbers.FirstOrDefault(number => number >= 30);

オブジェクトを検索する場合は、次のように記述できます。

User? user = users.FirstOrDefault(x => x.Id == 2);

if (user != null){Console.WriteLine(user.Name);}

9. C# Listをソートする方法

9-1. Sortで昇順に並び替える

Listを昇順に並び替えるには、Sortメソッドを使用します。

List<int> numbers = new List<int>{30,10,50,20,40};

numbers.Sort();

実行後は次の順番になります。

1020304050

文字列のListにも使用できます。

List<string> names = new List<string>{"Suzuki","Tanaka","Sato"};

names.Sort();

Sortは、元のListの順番を直接変更します。

9-2. Reverseで逆順にする

現在の順番を反転するには、Reverseメソッドを使用します。

List<int> numbers = new List<int>{10,20,30};

numbers.Reverse();

実行後は30、20、10の順番になります。

降順に並べたい場合は、昇順にソートした後で反転できます。

numbers.Sort();numbers.Reverse();

ただし、Reverseは並び替えではなく、現在の並びを単純に逆転するメソッドです。

9-3. OrderByで並び替える

LINQのOrderByを使うと、元のListを変更せずに昇順の結果を作成できます。

using System.Linq;

List<int> numbers = new List<int>{30,10,50,20,40};

List<int> sortedNumbers = numbers.OrderBy(number => number).ToList();

元のnumbersの順番は変わりません。並び替えた新しいListがsortedNumbersに格納されます。

9-4. OrderByDescendingで降順に並び替える

降順に並び替えるには、OrderByDescendingを使用します。

List<int> sortedNumbers = numbers.OrderByDescending(number => number).ToList();

結果は50、40、30、20、10の順番になります。

9-5. オブジェクトListを特定のプロパティでソートする

オブジェクトのListは、特定のプロパティを指定して並び替えられます。

class Product{public string Name { get; set; } = "";public int Price { get; set; }}
List<Product> products = new List<Product>{new Product { Name = "マウス", Price = 3000 },new Product { Name = "キーボード", Price = 8000 },new Product { Name = "モニター", Price = 25000 }};

List<Product> sortedProducts = products.OrderBy(product => product.Price).ToList();

価格が高い順に並べる場合は、OrderByDescendingを使用します。

List<Product> sortedProducts = products.OrderByDescending(product => product.Price).ToList();

複数の条件で並び替える場合は、ThenByThenByDescendingを組み合わせます。

List<Product> sortedProducts = products.OrderBy(product => product.Price).ThenBy(product => product.Name).ToList();

10. C# Listでよく使う便利メソッド

10-1. ToArrayで配列に変換する

Listを配列に変換するには、ToArrayメソッドを使用します。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

string[] nameArray = names.ToArray();

Listから作成された配列は、元のListとは別のコレクションです。

10-2. ToListでListに変換する

配列やLINQの処理結果をListに変換するには、ToListを使用します。

using System.Linq;

string[] nameArray ={"田中","佐藤","鈴木"};

List<string> names = nameArray.ToList();

LINQの検索結果にもよく使用します。

List<int> results = numbers.Where(number => number >= 30).ToList();

10-3. Anyで要素の有無を判定する

Listに要素が1件以上あるか確認するには、LINQのAnyを使用できます。

bool hasItem = names.Any();

条件に一致する要素があるか確認することもできます。

bool hasLongName = names.Any(name => name.Length >= 3);

単純に空かどうかを確認する場合は、Count > 0でも判定できます。

if (names.Count > 0){Console.WriteLine("要素があります。");}

10-4. SelectでListの値を変換する

List内の各要素を別の値へ変換するには、Selectを使用します。

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

List<int> doubledNumbers = numbers.Select(number => number * 2).ToList();

結果は2、4、6になります。

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

List<string> texts = numbers.Select(number => $"番号: {number}").ToList();

オブジェクトから特定のプロパティだけを取り出す場合にも便利です。

List<string> productNames = products.Select(product => product.Name).ToList();

10-5. Distinctで重複を削除する

重複した値を取り除くには、Distinctを使用します。

List<int> numbers = new List<int>{10,20,10,30,20};

List<int> uniqueNumbers = numbers.Distinct().ToList();

結果は10、20、30になります。

大文字と小文字を区別せずに文字列の重複を削除する場合は、比較方法を指定できます。

List<string> codes = new List<string>{"ABC","abc","DEF"};

List<string> uniqueCodes = codes.Distinct(StringComparer.OrdinalIgnoreCase).ToList();

10-6. Countで条件に一致する件数を取得する

LINQのCountを使うと、条件に一致する要素数を取得できます。

List<int> scores = new List<int>{45,60,75,90};

int passedCount = scores.Count(score => score >= 60);

Console.WriteLine(passedCount); // 3

List<T>Countプロパティは全要素数を返しますが、LINQのCountメソッドは条件を指定できます。

11. C# ListとLINQを組み合わせた実践的な使い方

11-1. 条件に一致するデータだけ抽出する

LINQのWhereを使うと、条件に一致するデータだけを抽出できます。

List<Product> products = new List<Product>{new Product { Name = "マウス", Price = 3000 },new Product { Name = "キーボード", Price = 8000 },new Product { Name = "モニター", Price = 25000 }};

List<Product> affordableProducts = products.Where(product => product.Price <= 10000).ToList();

この例では、価格が10,000円以下の商品だけを取得しています。

11-2. List内のデータを加工する

Selectを使うと、List内のデータを別の形式に加工できます。

List<string> displayTexts = products.Select(product => $"{product.Name}: {product.Price:N0}円").ToList();

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

マウス: 3,000円キーボード: 8,000円モニター: 25,000円

元のオブジェクトとは異なる型へ変換することも可能です。

var summaries = products.Select(product => new{product.Name,TaxIncludedPrice = (int)(product.Price * 1.1)}).ToList();

11-3. 複数条件で検索する

複数の条件は、&&||で組み合わせます。

List<Product> results = products.Where(product =>product.Price >= 5000 &&product.Price <= 20000).ToList();

商品名にも条件を指定できます。

string keyword = "キー";

List<Product> results = products.Where(product =>product.Price <= 10000 &&product.Name.Contains(keyword)).ToList();

&&は「かつ」、||は「または」を表します。

11-4. ソートと検索を組み合わせる

WhereOrderByをつなげると、条件検索した結果を並び替えられます。

List<Product> results = products.Where(product => product.Price >= 5000).OrderBy(product => product.Price).ToList();

この例では、価格が5,000円以上の商品を抽出し、価格の安い順に並び替えています。

処理をつなげる順番によって、コードの意味がわかりやすくなります。

List<string> names = products.Where(product => product.Price >= 5000).OrderByDescending(product => product.Price).Select(product => product.Name).ToList();

11-5. 集計処理にListを使う

LINQには、合計、平均、最大値、最小値などを求めるメソッドがあります。

int totalPrice = products.Sum(product => product.Price);double averagePrice = products.Average(product => product.Price);int maximumPrice = products.Max(product => product.Price);int minimumPrice = products.Min(product => product.Price);

空のListに対してAverageMaxMinを実行すると例外になるため、事前に要素の有無を確認します。

if (products.Any()){double averagePrice = products.Average(product => product.Price);Console.WriteLine(averagePrice);}

カテゴリごとに集計したい場合は、GroupByを使用できます。

var groupedProducts = products.GroupBy(product => product.Price >= 10000 ? "高価格" : "低価格").Select(group => new{Category = group.Key,Count = group.Count()}).ToList();

12. C# Listの実践サンプルコード

12-1. 文字列Listを作成して追加・削除・検索する例

次のサンプルでは、文字列Listに対する追加、削除、検索、一覧表示を行います。

using System;using System.Collections.Generic;

class Program{static void Main(){List<string> fruits = new List<string>{"りんご","みかん"};

    fruits.Add("バナナ");fruits.AddRange(new[] { "ぶどう", "いちご" });fruits.Remove("みかん");bool exists = fruits.Contains("バナナ");Console.WriteLine($"バナナがある: {exists}");Console.WriteLine("果物一覧:");foreach (string fruit in fruits){Console.WriteLine(fruit);}}

}

12-2. 数値Listをソートする例

次のサンプルでは、整数のListを昇順と降順に並び替えます。

using System;using System.Collections.Generic;using System.Linq;

class Program{static void Main(){List<int> numbers = new List<int>{50,10,40,20,30};

    List&lt;int&gt; ascending = numbers.OrderBy(number =&gt; number).ToList();List&lt;int&gt; descending = numbers.OrderByDescending(number =&gt; number).ToList();Console.WriteLine("昇順:");Console.WriteLine(string.Join(", ", ascending));Console.WriteLine("降順:");Console.WriteLine(string.Join(", ", descending));}

}

12-3. クラスのオブジェクトをListで管理する例

次のサンプルでは、商品クラスのオブジェクトをListで管理します。

using System;using System.Collections.Generic;using System.Linq;

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

class Program{static void Main(){List<Product> products = new List<Product>{new Product { Id = 1, Name = "マウス", Price = 3000 },new Product { Id = 2, Name = "キーボード", Price = 8000 },new Product { Id = 3, Name = "モニター", Price = 25000 }};

    Product? product = products.FirstOrDefault(x =&gt; x.Id == 2);if (product != null){product.Price = 7500;}foreach (Product item in products){Console.WriteLine($"{item.Id}: {item.Name} - {item.Price:N0}円");}}

}

12-4. ユーザー一覧から条件検索する例

年齢と有効状態を条件にユーザーを検索する例です。

using System;using System.Collections.Generic;using System.Linq;

class User{public int Id { get; set; }public string Name { get; set; } = "";public int Age { get; set; }public bool IsActive { get; set; }}

class Program{static void Main(){List<User> users = new List<User>{new User{Id = 1,Name = "田中",Age = 25,IsActive = true},new User{Id = 2,Name = "佐藤",Age = 17,IsActive = true},new User{Id = 3,Name = "鈴木",Age = 32,IsActive = false},new User{Id = 4,Name = "高橋",Age = 28,IsActive = true}};

    List&lt;User&gt; results = users.Where(user =&gt; user.Age &gt;= 20 &amp;&amp; user.IsActive).OrderBy(user =&gt; user.Age).ToList();foreach (User user in results){Console.WriteLine($"{user.Name}: {user.Age}歳");}}

}

12-5. Listを使った簡単なデータ管理プログラム

次のサンプルでは、コンソールから入力されたタスクをListで管理します。

using System;using System.Collections.Generic;

class Program{static void Main(){List<string> tasks = new List<string>();

    while (true){Console.WriteLine();Console.WriteLine("1: 追加");Console.WriteLine("2: 一覧表示");Console.WriteLine("3: 削除");Console.WriteLine("0: 終了");Console.Write("操作を選択してください: ");string? command = Console.ReadLine();if (command == "0"){break;}switch (command){case "1":Console.Write("追加するタスク: ");string? task = Console.ReadLine();if (!string.IsNullOrWhiteSpace(task)){tasks.Add(task);Console.WriteLine("タスクを追加しました。");}else{Console.WriteLine("タスクを入力してください。");}break;case "2":if (tasks.Count == 0){Console.WriteLine("タスクはありません。");break;}for (int i = 0; i &lt; tasks.Count; i++){Console.WriteLine($"{i + 1}: {tasks<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>}");}break;case "3":Console.Write("削除する番号: ");string? input = Console.ReadLine();if (int.TryParse(input, out int number)){int index = number - 1;if (index &gt;= 0 &amp;&amp; index &lt; tasks.Count){tasks.RemoveAt(index);Console.WriteLine("タスクを削除しました。");}else{Console.WriteLine("存在しない番号です。");}}else{Console.WriteLine("数値を入力してください。");}break;default:Console.WriteLine("正しい操作を選択してください。");break;}}}

}

13. C# Listを使うときの注意点

13-1. Listは0番目から始まる

Listのインデックスは0から始まります。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

各要素のインデックスは次のとおりです。

names[0] = 田中names[1] = 佐藤names[2] = 鈴木

要素数が3の場合、最後のインデックスは2です。names[3]へアクセスすると例外が発生します。

13-2. nullの扱いに注意する

List変数自体がnullの場合、AddCountなどを使用するとNullReferenceExceptionが発生します。

List<string>? names = null;

// names.Add("田中"); // 例外

Listを使用する前に初期化します。

List<string> names = new List<string>();

外部からnullを受け取る可能性がある場合は、null合体演算子を使えます。

List<string> names = receivedNames ?? new List<string>();

Listの要素としてnullが入る可能性にも注意が必要です。

List<string?> names = new List<string?>{"田中",null,"佐藤"};

foreach (string? name in names){if (name != null){Console.WriteLine(name.Length);}}

13-3. 大量データを扱う場合のパフォーマンス

Listは内部的に配列を利用しています。要素が増えて容量を超えると、より大きな内部配列を確保して要素をコピーします。

あらかじめおおよその件数がわかっている場合は、初期容量を指定できます。

List<int> numbers = new List<int>(10000);

これは要素数を10,000件にするコードではなく、10,000件分の容量をあらかじめ確保するコードです。作成直後のCountは0です。

Console.WriteLine(numbers.Count);    // 0Console.WriteLine(numbers.Capacity); // 10000

また、Listの途中へ頻繁に追加・削除すると、それより後ろにある要素の移動が発生します。大量データを扱う場合は、処理内容に応じてDictionary<TKey, TValue>HashSet<T>LinkedList<T>なども検討します。

13-4. 配列とListの使い分け

要素数が固定されていて変更する必要がない場合は、配列が適しています。

string[] weekdays ={"月","火","水","木","金","土","日"};

要素数が変化する場合は、Listが適しています。

List<string> participants = new List<string>();

participants.Add("田中");participants.Add("佐藤");participants.Remove("田中");

主な判断基準は次のとおりです。

  • 要素数が固定されている場合は配列

  • 要素の追加や削除がある場合はList

  • 可変長の検索結果を保持する場合はList

  • 外部APIなどが配列を要求する場合は配列

  • LINQで加工しながら管理する場合はList

13-5. スレッドセーフではない点に注意する

List<T>は、複数のスレッドから同時に更新することを前提としたコレクションではありません。

一方のスレッドで要素を追加し、別のスレッドで削除すると、データの不整合や例外が発生する可能性があります。

共有Listを複数スレッドから更新する場合は、lockで同期します。

private static readonly object SyncObject = new object();private static readonly List<string> Names = new List<string>();

static void AddName(string name){lock (SyncObject){Names.Add(name);}}

用途によっては、System.Collections.Concurrent名前空間にある並行コレクションの利用も検討してください。

14. C# Listでよくあるエラーと解決方法

14-1. IndexOutOfRangeExceptionが発生する原因

配列で範囲外のインデックスへアクセスすると、一般的にIndexOutOfRangeExceptionが発生します。一方、List<T>のインデクサーで範囲外を指定した場合は、通常ArgumentOutOfRangeExceptionが発生します。

List<string> names = new List<string>{"田中","佐藤"};

// Console.WriteLine(names[2]); // ArgumentOutOfRangeException

Listの要素数が2の場合、有効なインデックスは0と1です。

次のように範囲を確認してからアクセスします。

int index = 2;

if (index >= 0 && index < names.Count){Console.WriteLine(names[index]);}

for文の条件にも注意してください。

for (int i = 0; i < names.Count; i++){Console.WriteLine(names[i]);}

i <= names.Countではなく、i < names.Countにします。

14-2. NullReferenceExceptionが発生する原因

List変数が初期化されていない状態でメソッドやプロパティを使用すると、NullReferenceExceptionが発生します。

List<string>? names = null;

// Console.WriteLine(names.Count); // 例外

Listを作成してから使用します。

List<string> names = new List<string>();

メソッドの引数などでnullが渡される可能性がある場合は、チェックを行います。

static void PrintNames(List<string>? names){if (names == null){Console.WriteLine("Listがnullです。");return;}

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

}

14-3. foreach中の削除でエラーになる原因

foreachで列挙中のListを変更すると、コレクションの状態が変化するため、InvalidOperationExceptionが発生します。

// foreach (string name in names)// {//     if (name == "佐藤")//     {//         names.Remove(name);//     }// }

条件に一致する要素を削除する場合は、RemoveAllを使用します。

names.RemoveAll(name => name == "佐藤");

複雑な条件で削除する場合は、削除対象を別のListとして取得してから削除する方法もあります。

List<string> targets = names.Where(name => name.StartsWith("佐")).ToList();

foreach (string target in targets){names.Remove(target);}

14-4. 型が一致しないときのエラー

Listに指定した型と異なるデータは追加できません。

List<int> numbers = new List<int>();

// numbers.Add("100"); // コンパイルエラー

文字列を数値として追加したい場合は、変換処理が必要です。

string text = "100";

if (int.TryParse(text, out int number)){numbers.Add(number);}else{Console.WriteLine("数値に変換できませんでした。");}

継承関係のある型を扱う場合は、基底クラスやインターフェースを型パラメータに指定できます。

class Animal{public string Name { get; set; } = "";}

class Dog : Animal{}

class Cat : Animal{}

List<Animal> animals = new List<Animal>{new Dog { Name = "ポチ" },new Cat { Name = "タマ" }};

14-5. 初心者がつまずきやすいポイント

初心者がC# Listでつまずきやすいポイントには、次のようなものがあります。

1つ目は、Listを初期化せずに使用することです。

List<string> names = new List<string>();

2つ目は、インデックスが0から始まることを忘れることです。

string firstName = names[0];

3つ目は、Countを最後のインデックスとして使用することです。最後のインデックスはCount - 1です。

if (names.Count > 0){string lastName = names[names.Count - 1];}

4つ目は、RemoveRemoveAtを混同することです。

names.Remove("田中"); // 値を指定names.RemoveAt(0);    // インデックスを指定

5つ目は、LINQの処理結果を元のListに反映したと誤解することです。

names.OrderBy(name => name);

このコードだけでは、namesの順番は変わりません。結果を使用するには、変数へ代入します。

names = names.OrderBy(name => name).ToList();

15. C# Listに関するよくある質問

15-1. Listと配列はどちらを使うべきか

要素数が固定されていて、追加や削除を行わない場合は配列が適しています。要素数が変化する場合や、追加・削除・検索を頻繁に行う場合はListが便利です。

たとえば、1週間の曜日は要素数が固定されているため配列で管理できます。

string[] weekdays ={"月","火","水","木","金","土","日"};

一方、参加者一覧のように人数が変化するデータにはListが向いています。

List<string> participants = new List<string>();

迷った場合は、実行中に要素数が変わるかどうかを判断基準にするとよいでしょう。

15-2. Listの最大要素数はどれくらいか

Listに格納できる要素数は、実行環境、プロセスが利用できるメモリ、要素の型、配列の実装上の制限などに左右されます。

理論上の上限だけでなく、実際にはメモリ不足になる可能性を考える必要があります。数百万件以上のデータを扱う場合は、すべてをListへ読み込むのではなく、次のような方法を検討します。

  • データベース側で絞り込む

  • ページ単位で読み込む

  • ストリームとして順番に処理する

  • IEnumerable<T>で遅延実行する

  • 不要なデータを保持し続けない

  • 用途に適したデータ構造を選ぶ

大量データでは、要素数だけでなく各要素が使用するメモリ量も重要です。

15-3. Listに異なる型のデータを入れられるか

通常のList<T>には、指定した型と互換性のあるデータだけを格納します。

List<string> values = new List<string>();

values.Add("文字列");// values.Add(100); // 追加できない

List<object>を使えば、異なる型のデータを格納できます。

List<object> values = new List<object>{"田中",100,true};

ただし、取り出すときに型の確認や変換が必要になるため、コードが複雑になりやすくなります。

foreach (object value in values){if (value is string text){Console.WriteLine($"文字列: {text}");}else if (value is int number){Console.WriteLine($"整数: {number}");}}

関連するデータをまとめたい場合は、List<object>よりも専用クラスを作る方法が一般的です。

15-4. Listの中身を一括で表示するにはどうするか

文字列や数値のListは、string.Joinを使うと簡単に一括表示できます。

List<string> names = new List<string>{"田中","佐藤","鈴木"};

Console.WriteLine(string.Join(", ", names));

実行結果は次のとおりです。

田中, 佐藤, 鈴木

1行ずつ表示したい場合は、foreachを使用します。

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

オブジェクトListの場合は、表示したい項目をSelectで文字列に変換できます。

Console.WriteLine(string.Join(Environment.NewLine,products.Select(product => $"{product.Name}: {product.Price:N0}円")));

15-5. Listをコピーするにはどうするか

Listの要素を新しいListへコピーするには、コンストラクターへ元のListを渡します。

List<string> original = new List<string>{"田中","佐藤","鈴木"};

List<string> copy = new List<string>(original);

ToListでもコピーできます。

List<string> copy = original.ToList();

文字列や数値のような値を扱う場合は、この方法で独立したListを作成できます。

ただし、クラスのオブジェクトを格納したListでは、List自体は別になりますが、各要素は同じオブジェクトを参照します。

List<User> copiedUsers = new List<User>(users);

copiedUsers[0].Name = "変更後";

この場合、元のusers[0].Nameも変更されます。オブジェクトまで別にしたい場合は、新しいインスタンスを作成します。

List<User> copiedUsers = users.Select(user => new User{Id = user.Id,Name = user.Name,Age = user.Age,IsActive = user.IsActive}).ToList();

このようにオブジェクトの内容も複製する方法は、一般にディープコピーと呼ばれます。クラス内に別の参照型プロパティがある場合は、それらも必要に応じてコピーしなければなりません。

まとめ

C#のList<T>は、複数のデータを柔軟に管理できる代表的なコレクションです。配列とは異なり、実行中に要素を追加・削除できるため、ユーザー一覧、商品一覧、検索結果など、件数が変化するデータに適しています。

Listの基本操作では、次のメソッドやプロパティをよく使用します。

  • AddAddRangeで要素を追加する

  • インデックスやforeachで要素を取得する

  • Countで要素数を確認する

  • RemoveRemoveAtRemoveAllで要素を削除する

  • ContainsFindFindAllで要素を検索する

  • SortOrderByで並び替える

  • SelectWhereDistinctでデータを加工する

  • ToArrayToListでコレクションを変換する

Listを扱うときは、インデックスが0から始まること、範囲外のインデックスを指定しないこと、nullを適切に処理すること、foreach中にListを変更しないことが重要です。

まずはAddCountforeachRemoveから使い始め、慣れてきたらLINQのWhereSelectOrderByを組み合わせてみましょう。これらを身につけることで、C#のデータ処理を簡潔で読みやすく記述できるようになります。