C#の配列(Array)入門|宣言・初期化・要素の追加・Listとの違いまでわかりやすく解説
はじめに
C#で複数のデータをまとめて扱うときに使われる基本的な仕組みが、配列(Array)です。たとえば、複数の点数や商品名、ユーザーIDなどを1つの変数で管理したい場合に配列が役立ちます。
配列はC#プログラミングの基礎として重要ですが、初めて学ぶ方は「どのように宣言・初期化するのか」「要素を追加できるのか」「Listとは何が違うのか」といった点で迷いやすいでしょう。
この記事では、C#の配列について、基本的な使い方から要素の取得・変更、要素数の確認、繰り返し処理、要素を追加する方法、Listとの違いまでわかりやすく解説します。
1. C#の配列(Array)とは
C#の配列とは、同じ型のデータを複数まとめて管理するためのデータ構造です。
たとえば、3人分の点数を個別の変数で管理すると、次のようになります。
C#int score1 = 80;
int score2 = 90;
int score3 = 75;
データが少ないうちは問題ありませんが、人数が増えると変数を何個も用意しなければなりません。配列を使えば、複数の値を1つの変数にまとめられます。
C#int[] scores = { 80, 90, 75 };
この例では、scoresという配列に3つの整数を格納しています。
C#では配列もオブジェクトであり、すべての配列はSystem.Arrayクラスを継承しています。そのため、要素数の取得や並べ替え、検索といった操作も行えます。
配列の特徴
C#の配列には、主に次の特徴があります。
同じ型の値を複数格納できる
要素には番号を指定してアクセスする
作成した後は要素数を基本的に変更できない
値型だけでなく、文字列や独自クラスも格納できる
1次元配列だけでなく、多次元配列やジャグ配列も作成できる
配列は要素数が固定されているため、格納するデータ数があらかじめ決まっている場面に適しています。
C#で配列を宣言する方法
配列を宣言する基本構文は次のとおりです。
C#データ型[] 配列名;
整数を格納する配列は、次のように宣言します。
C#int[] numbers;
文字列を格納する配列の場合は、次のようになります。
C#string[] names;
この時点では変数を宣言しただけで、配列本体は作成されていません。配列を使用するには、newを使って配列を生成します。
C#int[] numbers = new int[3];
new int[3]は、整数を3個格納できる配列を作成するという意味です。
配列を初期化する方法
C#の配列を初期化する方法はいくつかあります。
要素数を指定して初期化する
C#int[] numbers = new int[3];
要素数だけを指定した場合、各要素には型ごとの既定値が設定されます。
intの既定値は0です。
C#Console.WriteLine(numbers[0]); // 0
Console.WriteLine(numbers[1]); // 0
Console.WriteLine(numbers[2]); // 0
主な型の既定値は次のとおりです。
| 型 | 既定値 |
|---|---|
int | 0 |
double | 0.0 |
bool | false |
string | null |
| クラス型 | null |
宣言と同時に値を指定する
配列に格納する値が決まっている場合は、宣言と同時に初期化できます。
C#int[] numbers = new int[] { 10, 20, 30 };
要素数は初期値から自動的に判断されるため、次のように省略することも可能です。
C#int[] numbers = { 10, 20, 30 };
文字列の配列も同じように初期化できます。
C#string[] fruits = { "りんご", "みかん", "バナナ" };
varを使って型を推論させる場合は、new[]を使用します。
C#var numbers = new[] { 10, 20, 30 };
この場合、すべての要素から配列の型が推論され、numbersはint[]になります。
配列の要素を取得する方法
配列の各要素には、インデックスと呼ばれる番号を指定してアクセスします。
C#string[] fruits = { "りんご", "みかん", "バナナ" };
Console.WriteLine(fruits[0]); // りんご
Console.WriteLine(fruits[1]); // みかん
Console.WriteLine(fruits[2]); // バナナ
C#の配列では、最初の要素のインデックスが0になる点に注意しましょう。
3要素の配列で使用できるインデックスは、0、1、2です。存在しないインデックスを指定すると、IndexOutOfRangeExceptionが発生します。
C#int[] numbers = { 10, 20, 30 };
Console.WriteLine(numbers[3]); // 例外が発生
配列の最後の要素を取得するときは、Lengthを利用できます。
C#int lastNumber = numbers[numbers.Length - 1];
Console.WriteLine(lastNumber); // 30
C# 8.0以降では、インデックス演算子^を使った書き方も可能です。
C#int lastNumber = numbers[^1];
Console.WriteLine(lastNumber); // 30
^1は末尾から1番目、^2は末尾から2番目の要素を表します。
配列の要素を変更する方法
配列の要素は、インデックスを指定して代入することで変更できます。
C#int[] numbers = { 10, 20, 30 };
numbers[1] = 100;
Console.WriteLine(numbers[1]); // 100
配列自体の要素数は固定ですが、各要素に格納されている値は変更できます。
ただし、読み取り専用の用途で使用したい場合は、配列をそのまま外部に公開すると書き換えられる可能性があります。その場合は、ReadOnlyCollection<T>やIReadOnlyList<T>などの利用を検討しましょう。
配列の要素数を取得する方法
配列の要素数は、Lengthプロパティで取得できます。
C#int[] numbers = { 10, 20, 30 };
Console.WriteLine(numbers.Length); // 3
Lengthは、配列を繰り返し処理するときにもよく使用します。
C#for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}
Lengthと似たものに、LINQのCount()メソッドがあります。
C#using System.Linq;
int count = numbers.Count();
単純に配列の要素数を取得するだけであれば、メソッド呼び出しが不要なLengthを使うのが一般的です。
配列をfor文で繰り返し処理する
配列のインデックスが必要な場合は、for文が便利です。
C#string[] names = { "田中", "佐藤", "鈴木" };
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine($"{i}: {names[i]}");
}
実行結果は次のようになります。
0: 田中
1: 佐藤
2: 鈴木
for文ではインデックスを利用できるため、特定の位置にある要素を変更する処理にも向いています。
C#int[] numbers = { 1, 2, 3 };
for (int i = 0; i < numbers.Length; i++)
{
numbers[i] *= 2;
}
処理後の配列は、2、4、6になります。
配列をforeach文で繰り返し処理する
配列の要素を先頭から順番に読み取るだけであれば、foreach文を使うと簡潔に書けます。
C#string[] fruits = { "りんご", "みかん", "バナナ" };
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
型を明示せず、varを使うこともできます。
C#foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}
foreach文の変数に別の値を代入して、配列の要素を直接変更することはできません。要素を書き換える場合は、インデックスを使用できるfor文を利用します。
C#の配列に要素を追加する方法
C#の配列は、作成時に要素数が決まります。そのため、JavaScriptのpushのように、既存の配列へ要素を直接追加することはできません。
C#int[] numbers = { 10, 20, 30 };
この配列の要素数は3で固定されています。
要素を追加したい場合は、より大きな新しい配列を作成し、既存の要素をコピーする必要があります。
Array.Resizeを使う
Array.Resizeを使うと、配列のサイズを変更したような処理を簡単に記述できます。
C#int[] numbers = { 10, 20, 30 };
Array.Resize(ref numbers, numbers.Length + 1);
numbers[^1] = 40;
処理後の配列は次のようになります。
10, 20, 30, 40
ただし、Array.Resizeが既存の配列そのものを拡張しているわけではありません。内部では新しい配列を作成し、元の要素をコピーして変数に再代入しています。
要素を何度も追加すると、そのたびに新しい配列の作成とコピーが発生します。頻繁に追加・削除する場合は、配列ではなくList<T>を使用するのが適切です。
新しい配列へコピーする
新しい配列を作成し、Array.Copyで既存の要素をコピーする方法もあります。
C#int[] numbers = { 10, 20, 30 };
int[] newNumbers = new int[numbers.Length + 1];
Array.Copy(numbers, newNumbers, numbers.Length);
newNumbers[^1] = 40;
numbers = newNumbers;
処理内容が明確になる一方、単純なサイズ変更であればArray.Resizeのほうが簡潔です。
LINQのAppendを使う
LINQのAppendを使って、要素を末尾に追加した新しい配列を作成することもできます。
C#using System.Linq;
int[] numbers = { 10, 20, 30 };
numbers = numbers.Append(40).ToArray();
Appendは元の配列を変更しません。要素を追加した新しいシーケンスを返すため、配列として使用するにはToArray()が必要です。
複数の要素を追加する場合は、Concatを利用できます。
C#int[] numbers = { 10, 20, 30 };
int[] additions = { 40, 50 };
numbers = numbers.Concat(additions).ToArray();
これらの方法も新しい配列を生成するため、頻繁な追加処理には向いていません。
配列から要素を削除する方法
配列には、特定の要素を直接削除するメソッドもありません。削除後の要素だけを含む新しい配列を作成します。
たとえば、値が20の要素を除外する場合は、LINQのWhereを使用できます。
C#using System.Linq;
int[] numbers = { 10, 20, 30 };
numbers = numbers
.Where(number => number != 20)
.ToArray();
結果は次のとおりです。
10, 30
同じ値が複数存在する場合、この書き方では条件に一致するすべての要素が削除されます。
特定のインデックスにある要素だけを削除する場合は、インデックスを条件にします。
C#int removeIndex = 1;
numbers = numbers
.Where((number, index) => index != removeIndex)
.ToArray();
追加と同様に、削除を頻繁に行う場合はList<T>の利用を検討しましょう。
配列を検索する方法
配列内から特定の要素を探す場合は、Array.IndexOfを使用できます。
C#string[] fruits = { "りんご", "みかん", "バナナ" };
int index = Array.IndexOf(fruits, "みかん");
Console.WriteLine(index); // 1
対象の要素が存在しない場合は、-1が返されます。
条件に一致する要素を検索する場合は、Array.Findが便利です。
C#int[] numbers = { 10, 25, 30, 45 };
int result = Array.Find(numbers, number => number > 20);
Console.WriteLine(result); // 25
条件に一致するすべての要素を取得する場合は、Array.FindAllを使用します。
C#int[] results = Array.FindAll(numbers, number => number > 20);
配列に値が含まれているかどうかだけを確認する場合は、LINQのContainsも使用できます。
C#using System.Linq;
bool exists = fruits.Contains("バナナ");
Console.WriteLine(exists); // True
配列を並べ替える方法
配列を昇順に並べ替えるには、Array.Sortを使用します。
C#int[] numbers = { 30, 10, 20 };
Array.Sort(numbers);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
10
20
30
Array.Sortは元の配列を直接変更します。
降順に並べたい場合は、昇順に並べ替えた後でArray.Reverseを実行できます。
C#Array.Sort(numbers);
Array.Reverse(numbers);
元の配列を変更したくない場合は、LINQのOrderByやOrderByDescendingを使用して新しい配列を作成します。
C#using System.Linq;
int[] sortedNumbers = numbers
.OrderBy(number => number)
.ToArray();
降順の場合は次のように記述します。
C#int[] sortedNumbers = numbers
.OrderByDescending(number => number)
.ToArray();
2次元配列の使い方
C#では、表のように行と列を持つ2次元配列を作成できます。
C#int[,] matrix =
{
{ 1, 2, 3 },
{ 4, 5, 6 }
};
要素を取得するときは、行と列のインデックスを指定します。
C#Console.WriteLine(matrix[0, 0]); // 1
Console.WriteLine(matrix[1, 2]); // 6
2次元配列の各次元の長さは、GetLengthメソッドで取得できます。
C#int rows = matrix.GetLength(0);
int columns = matrix.GetLength(1);
すべての要素を処理する場合は、二重のfor文を使用します。
C#for (int row = 0; row < matrix.GetLength(0); row++)
{
for (int column = 0; column < matrix.GetLength(1); column++)
{
Console.WriteLine(matrix[row, column]);
}
}
2次元配列は、座席表、ゲームのマップ、表形式の数値データなどを扱う場面で利用できます。
ジャグ配列の使い方
ジャグ配列とは、配列の各要素が別の配列になっている構造です。配列の配列とも呼ばれます。
C#int[][] numbers =
{
new int[] { 1, 2 },
new int[] { 3, 4, 5 },
new int[] { 6 }
};
各行の要素数を異なる長さにできる点が、2次元配列との主な違いです。
要素には、2回インデックスを指定してアクセスします。
C#Console.WriteLine(numbers[0][1]); // 2
Console.WriteLine(numbers[1][2]); // 5
すべての要素を処理する場合は、次のように記述できます。
C#foreach (int[] row in numbers)
{
foreach (int number in row)
{
Console.WriteLine(number);
}
}
行ごとに要素数が異なるデータを扱う場合は、2次元配列よりジャグ配列が適しています。
C#の配列とListの違い
C#で複数の値を扱う方法には、配列のほかにList<T>があります。
配列とListの大きな違いは、要素数を変更できるかどうかです。
| 比較項目 | 配列 | List |
|---|---|---|
| 宣言例 | int[] | List<int> |
| 要素数 | 固定 | 変更可能 |
| 要素の追加 | 新しい配列が必要 | Addで追加可能 |
| 要素の削除 | 新しい配列が必要 | Removeなどで削除可能 |
| 要素数の取得 | Length | Count |
| インデックスアクセス | 可能 | 可能 |
| 主な用途 | 件数が固定されたデータ | 件数が増減するデータ |
List<T>を使用するには、通常、System.Collections.Generic名前空間を利用します。
C#using System.Collections.Generic;
List<int> numbers = new List<int> { 10, 20, 30 };
要素はAddメソッドで追加できます。
C#numbers.Add(40);
複数の要素を追加する場合は、AddRangeを使用します。
C#numbers.AddRange(new[] { 50, 60 });
要素の削除も簡単です。
C#numbers.Remove(20);
numbers.RemoveAt(0);
配列の要素数が固定されており、後から追加・削除しない場合は配列が適しています。一方、データ数が変化する場合や、追加・削除を頻繁に行う場合はList<T>が適しています。
配列をListへ変換する方法
配列は、ToListを使ってList<T>へ変換できます。
C#using System.Collections.Generic;
using System.Linq;
int[] array = { 10, 20, 30 };
List<int> list = array.ToList();
変換後は、AddやRemoveなどのメソッドを利用できます。
C#list.Add(40);
list.Remove(20);
Listを配列へ変換する方法
List<T>は、ToArrayメソッドで配列へ変換できます。
C#List<int> list = new List<int> { 10, 20, 30 };
int[] array = list.ToArray();
処理中は追加・削除しやすいListを使い、最終的に要素数が確定した段階で配列へ変換する方法もよく使われます。
配列を使用するときの注意点
C#の配列を扱う際は、次の点に注意しましょう。
インデックスは0から始まる
配列の最初の要素はarray[0]です。要素数が3の場合、最後のインデックスは2になります。
C#int[] numbers = new int[3];
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
ループ条件をi <= numbers.Lengthにすると、最後に存在しないインデックスへアクセスしてしまいます。
C#// 誤り
for (int i = 0; i <= numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}
正しくはi < numbers.Lengthです。
C#for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}
配列の変数には参照が格納される
配列は参照型です。別の変数へ代入すると、配列の内容が複製されるのではなく、同じ配列への参照が渡されます。
C#int[] array1 = { 10, 20, 30 };
int[] array2 = array1;
array2[0] = 100;
Console.WriteLine(array1[0]); // 100
独立した配列としてコピーしたい場合は、Cloneなどを利用します。
C#int[] array1 = { 10, 20, 30 };
int[] array2 = (int[])array1.Clone();
array2[0] = 100;
Console.WriteLine(array1[0]); // 10
nullの可能性を考慮する
配列変数にはnullが代入される可能性があります。
C#int[]? numbers = null;
nullの状態でLengthやインデックスへアクセスすると、NullReferenceExceptionが発生します。
C#if (numbers != null)
{
Console.WriteLine(numbers.Length);
}
空の配列を表現したい場合は、Array.Empty<T>()を使用できます。
C#int[] numbers = Array.Empty<int>();
この配列の要素数は0ですが、nullではありません。
C#Console.WriteLine(numbers.Length); // 0
戻り値として配列を返すメソッドでは、nullよりも空の配列を返したほうが、呼び出し側で扱いやすくなることがあります。
まとめ
C#の配列(Array)は、同じ型のデータを複数まとめて管理するための基本的なデータ構造です。int[]やstring[]のように型名の後ろへ[]を付けて宣言し、インデックスを使って各要素を取得・変更できます。
配列の要素数はLengthで確認でき、for文やforeach文を使って順番に処理できます。ただし、配列の要素数は作成時に固定されるため、既存の配列へ要素を直接追加することはできません。
要素を追加する場合は、Array.ResizeやLINQのAppendを使って新しい配列を作成できます。しかし、追加や削除を頻繁に行う用途では、AddやRemoveを利用できるList<T>のほうが適しています。
扱うデータ数が固定されている場合は配列、データ数が増減する場合はListというように、目的に応じて使い分けることが重要です。

