C#のnew Listの使い方を完全解説|初期化・追加・配列との違いまで初心者向けに紹介

はじめに

C#で複数のデータをまとめて扱うときによく使われるのが、List<T>です。new List<T>()と記述することで、要素を自由に追加・削除できるリストを作成できます。

配列も複数のデータを管理できますが、基本的に作成後の要素数を変更できません。一方、List<T>は必要に応じて要素数を増減できるため、データ数が事前に決まっていない場面に適しています。

この記事では、C#のnew Listの基本から、初期化、要素の追加、配列との違いまで、初心者にもわかりやすく解説します。

1. C#のnew Listとは?初心者向けに基本を解説

1-1. List<T>は複数のデータを管理するコレクション

List<T>は、同じ型のデータを複数まとめて管理するためのコレクションです。

たとえば、複数の整数を管理する場合は、次のように記述します。

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

文字列を管理する場合は、次のようになります。

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

List<T>には、主に次のような特徴があります。

  • 同じ型の要素を複数格納できる

  • 作成後でも要素を追加できる

  • 不要な要素を削除できる

  • インデックスを使って要素を取得できる

  • 要素数を自動的に拡張できる

配列では、作成するときに要素数を決める必要があります。

C#
int[] numbers = new int[3];

この配列には3つの要素しか格納できません。後から4つ目の要素が必要になった場合、新しい配列を作り直す必要があります。

一方、List<T>ではAddメソッドを使って、必要なだけ要素を追加できます。

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

numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
numbers.Add(40);

このように、データ数が変化する可能性がある場合は、配列よりもList<T>が便利です。

1-2. new List<T>()でリストのインスタンスを作成する

C#でリストを使用するには、一般的にnew List<T>()を使ってインスタンスを作成します。

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

このコードは、次の2つの部分に分けて考えると理解しやすくなります。

C#
List<string> names;

左側では、List<string>型の変数namesを宣言しています。

C#
new List<string>();

右側では、新しいList<string>のインスタンスを作成しています。

変数を宣言しただけでは、リスト本体は作成されていません。

C#
List<string> names = null;

この状態でAddメソッドを呼び出すと、NullReferenceExceptionが発生します。

C#
List<string> names = null;
names.Add("田中"); // 例外が発生する

リストを使用する前に、new List<string>()でインスタンスを作成する必要があります。

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

なお、C# 9以降では、変数の型が左側から判断できる場合に、右側の型名を省略するターゲット型のnewも使用できます。

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

これは、次のコードと同じ意味です。

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

初心者のうちは、型の関係がわかりやすいnew List<string>()の形から覚えるとよいでしょう。

1-3. Listの型を表す<T>の意味

List<T>Tは、リストに格納する要素の型を表します。

Tには、intstringなどの具体的な型を指定します。

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

それぞれのリストに格納できる値は、次のとおりです。

C#
List<int> numbers = new List<int>();
numbers.Add(100);

List<string> names = new List<string>();
names.Add("佐藤");

List<double> prices = new List<double>();
prices.Add(1980.5);

List<int>には整数、List<string>には文字列だけを追加できます。異なる型の値を追加しようとすると、コンパイルエラーになります。

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

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

List<T>のように、使用するときに型を指定する仕組みをジェネリックと呼びます。

独自に作成したクラスも、List<T>の型として指定できます。

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

Userクラスのリストは、次のように作成します。

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

要素を追加する場合は、Userのインスタンスを作成してAddメソッドに渡します。

C#
users.Add(new User
{
Name = "田中",
Age = 25
});

このように、List<T>は数値や文字列だけでなく、さまざまな型のデータをまとめて管理できます。

1-4. Listを使うために必要なusingディレクティブ

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

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

C#
using System.Collections.Generic;

これにより、クラス名だけでList<T>を使用できます。

C#
using System.Collections.Generic;

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

using System.Collections.Generic;を記述しない場合は、完全修飾名を使用する必要があります。

C#
System.Collections.Generic.List<string> names =
new System.Collections.Generic.List<string>();

完全修飾名でも動作しますが、コードが長くなります。通常はusing System.Collections.Generic;を記述する方法が一般的です。

.NET 6以降のプロジェクトでは、暗黙的なusingが有効になっていることがあります。その場合、ファイル内にusing System.Collections.Generic;を書かなくてもList<T>を使用できます。

ただし、プロジェクトの設定によって異なるため、Listが見つからないというエラーが表示された場合は、まずusing System.Collections.Generic;が記述されているか確認しましょう。

2. C#でnew Listを使ってリストを初期化する方法

2-1. 空のListを作成する基本構文

空のリストを作成する基本構文は、次のとおりです。

C#
List<> 変数名 = new List<>();

整数を格納する空のリストを作成する場合は、次のように記述します。

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

文字列を格納する場合は、次のようになります。

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

作成直後のリストには、要素が1つも入っていません。現在の要素数はCountプロパティで確認できます。

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

Console.WriteLine(names.Count);

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

0

Countは、リストに現在格納されている要素数を返します。

空のリストを作成しておけば、プログラムの処理中に必要なデータを順番に追加できます。

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

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

Console.WriteLine(names.Count);

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

2

2-2. コレクション初期化子で要素を指定する方法

リストを作成すると同時に要素を登録したい場合は、コレクション初期化子を使用します。

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

このコードでは、3つの文字列を持つリストを作成しています。

整数の場合も同じように記述できます。

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

丸括弧を付けた次の書き方も可能です。

C#
List<int> numbers = new List<int>()
{
10,
20,
30
};

引数のないコンストラクターを使用する場合、コレクション初期化子の前にある()は省略できます。

コレクション初期化子は、内部的には要素ごとにAddメソッドを呼び出す処理に相当します。

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

numbers.Add(10);
numbers.Add(20);
numbers.Add(30);

最初から登録するデータが決まっている場合は、コレクション初期化子を使うとコードを簡潔に書けます。

独自クラスのリストも初期化できます。

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

リストの初期化とオブジェクトの初期化を組み合わせることで、複数のデータをわかりやすく記述できます。

2-3. 既存の配列からListを作成する方法

List<T>のコンストラクターに配列を渡すと、配列の要素を持つ新しいリストを作成できます。

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

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

作成したリストには、配列の要素が順番に格納されます。

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

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

田中
佐藤
鈴木

配列から作成したリストには、後から要素を追加できます。

C#
names.Add("高橋");

元の配列と新しく作成したリストは、別のコレクションです。そのため、リストに要素を追加しても、元の配列の要素数は変わりません。

C#
string[] nameArray =
{
"田中",
"佐藤"
};

List<string> names = new List<string>(nameArray);
names.Add("鈴木");

Console.WriteLine(nameArray.Length);
Console.WriteLine(names.Count);

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

2
3

LINQを利用できる環境では、ToListメソッドを使うこともできます。

C#
using System.Linq;

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

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

ただし、単純に配列からリストを作成するだけであれば、new List<string>(nameArray)でも十分です。

2-4. 既存のListをコピーして新しいListを作成する方法

既存のリストをもとに、新しいリストを作成することもできます。

C#
List<string> originalNames = new List<string>
{
"田中",
"佐藤"
};

List<string> copiedNames = new List<string>(originalNames);

copiedNamesは、originalNamesとは別のリストです。そのため、コピー先に要素を追加しても、コピー元には影響しません。

C#
copiedNames.Add("鈴木");

Console.WriteLine(originalNames.Count);
Console.WriteLine(copiedNames.Count);

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

2
3

一方、次のように代入しただけでは、新しいリストは作成されません。

C#
List<string> originalNames = new List<string>
{
"田中",
"佐藤"
};

List<string> anotherNames = originalNames;

この場合、originalNamesanotherNamesは同じリストを参照しています。

C#
anotherNames.Add("鈴木");

Console.WriteLine(originalNames.Count);
Console.WriteLine(anotherNames.Count);

実行結果は、どちらも3です。

3
3

独立したリストとしてコピーしたい場合は、コンストラクターに既存のリストを渡しましょう。

C#
List<string> copiedNames = new List<string>(originalNames);

ただし、要素がクラスなどの参照型である場合、コピーされるのは各オブジェクトへの参照です。

C#
List<User> originalUsers = new List<User>
{
new User
{
Name = "田中",
Age = 25
}
};

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

copiedUsers[0].Name = "佐藤";

Console.WriteLine(originalUsers[0].Name);

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

佐藤

リスト自体は別でも、その中に格納されたUserオブジェクトは共有されています。このようなコピーはシャローコピーと呼ばれます。

オブジェクトまで完全に独立させたい場合は、それぞれの要素について新しいインスタンスを作成する必要があります。

C#
List<User> copiedUsers = originalUsers
.Select(user => new User
{
Name = user.Name,
Age = user.Age
})
.ToList();

このコードを使用する場合は、System.Linq名前空間が必要です。

2-5. 要素数を指定してListの容量を確保する方法

List<T>は、必要に応じて内部の容量を自動的に拡張します。しかし、登録する要素数があらかじめわかっている場合は、コンストラクターで初期容量を指定できます。

C#
List<int> numbers = new List<int>(100);

このコードは、100個分の要素を格納できる容量をあらかじめ確保します。

ここで注意したいのは、容量を100にしても、要素が100個追加されるわけではないことです。

C#
List<int> numbers = new List<int>(100);

Console.WriteLine(numbers.Count);
Console.WriteLine(numbers.Capacity);

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

0
100

CountCapacityには、次の違いがあります。

  • Countは、現在リストに格納されている要素数

  • Capacityは、内部的に確保されている格納可能な要素数

容量だけを指定したリストでは、インデックスを使って要素を代入できません。

C#
List<int> numbers = new List<int>(100);

numbers[0] = 10; // 例外が発生する

容量は確保されていますが、要素はまだ存在しないためです。要素を登録するときは、Addメソッドを使用します。

C#
List<int> numbers = new List<int>(100);

numbers.Add(10);
numbers.Add(20);

大量のデータを追加することが事前にわかっている場合、初期容量を指定すると、内部配列を拡張する回数を減らせる可能性があります。

ただし、通常の小規模な処理では、容量を細かく意識する必要はありません。まずは次の基本形を使えば十分です。

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

2-6. varを使ってListを宣言する方法

C#では、ローカル変数の型をコンパイラーに推論させるvarを使用できます。

C#
var numbers = new List<int>();

この場合、右側のnew List<int>()から、numbersList<int>型であると判断されます。

次の2つのコードは同じ意味です。

C#
List<int> numbers = new List<int>();
C#
var numbers = new List<int>();

コレクション初期化子と組み合わせることもできます。

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

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

そのため、後から別の型の値を代入することはできません。

C#
var names = new List<string>();

names = new List<int>(); // コンパイルエラー

また、varだけで変数を宣言することはできません。

C#
var names; // コンパイルエラー

初期値から型を判断できないためです。nullだけを代入する書き方もできません。

C#
var names = null; // コンパイルエラー

varはコードを短くできますが、型がわかりにくくなる場合もあります。右側を見れば型が明確にわかる場合に使用するとよいでしょう。

C#
var names = new List<string>();

3. new Listで作成したリストに要素を追加する方法

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

new List<T>()で作成したリストに要素を1つ追加するには、Addメソッドを使用します。

基本構文は次のとおりです。

C#
リスト名.Add(追加する値);

文字列のリストに要素を追加する場合は、次のように記述します。

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

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

追加された要素は、0から始まるインデックスを使って取得できます。

C#
Console.WriteLine(names[0]);
Console.WriteLine(names[1]);
Console.WriteLine(names[2]);

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

田中
佐藤
鈴木

リストの末尾に要素が追加されるため、追加した順番で格納されます。

整数のリストでも使い方は同じです。

C#
List<int> scores = new List<int>();

scores.Add(80);
scores.Add(90);
scores.Add(75);

foreach文を使うと、すべての要素を順番に処理できます。

C#
foreach (int score in scores)
{
Console.WriteLine(score);
}

変数の値を追加することもできます。

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

string userName = "高橋";
names.Add(userName);

独自クラスのオブジェクトを追加する場合は、次のように記述します。

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

User user = new User
{
Name = "田中",
Age = 25
};

users.Add(user);

オブジェクトの作成と追加を同時に記述することも可能です。

C#
users.Add(new User
{
Name = "佐藤",
Age = 30
});

List<T>には、宣言時に指定した型の値だけを追加できます。

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

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

文字列の"30"int型ではないため、List<int>には追加できません。文字列を数値として追加したい場合は、整数に変換します。

C#
string text = "30";
int number = int.Parse(text);

numbers.Add(number);

ユーザー入力など、変換できない文字列が含まれる可能性がある場合は、int.TryParseを使用すると安全です。

C#
string text = "30";

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

Addメソッドで追加した後は、Countプロパティの値も増加します。

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

Console.WriteLine(names.Count);

names.Add("田中");

Console.WriteLine(names.Count);

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

0
1

複数の要素をまとめて追加したい場合は、AddRangeメソッドを使用できます。

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

names.AddRange(new[]
{
"田中",
"佐藤",
"鈴木"
});

1つだけ追加するときはAdd、複数をまとめて追加するときはAddRangeと使い分けるとよいでしょう。

まとめ

C#のnew List<T>()は、要素数を柔軟に変更できるリストを作成するための基本構文です。

空のリストを作成する場合は、次のように記述します。

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

作成時に要素を登録する場合は、コレクション初期化子を使用します。

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

配列や既存のリストから、新しいリストを作成することも可能です。

C#
List<string> namesFromArray = new List<string>(nameArray);
List<string> copiedNames = new List<string>(originalNames);

作成したリストに要素を1つ追加するときは、Addメソッドを使用します。

C#
names.Add("高橋");

配列は基本的に作成後の要素数を変更できませんが、List<T>は必要に応じて要素を追加・削除できます。データ数が変化する処理では、List<T>を使うことで柔軟で読みやすいコードを記述できます。