C#の配列(array)完全ガイド|宣言・初期化・要素追加・多次元配列・Listとの違いを初心者向けに解説

はじめに

C#で複数のデータをまとめて扱うときに使われる基本的な機能が、配列(array)です。配列を使うと、複数の値を1つの変数で管理し、インデックスを指定して各要素を取得・変更できます。

一方で、C#の配列には「作成後に要素数を変更できない」という特徴があります。そのため、要素を追加・削除する可能性がある場合は、配列ではなくList<T>を使ったほうが適していることもあります。

この記事では、C#の配列について、宣言・初期化・要素の取得や変更・要素追加・多次元配列・ジャグ配列・List<T>との違いまで、初心者にもわかりやすく解説します。

1. C#の配列(array)とは

C#の配列とは、同じデータ型の値を複数まとめて格納するためのデータ構造です。

たとえば、3人分の点数を個別の変数で管理すると、次のようになります。

C#
int score1 = 80;
int score2 = 90;
int score3 = 75;

データ数が少ないうちは問題ありませんが、人数が増えると変数の管理が難しくなります。配列を使えば、次のように1つの変数へまとめられます。

C#
int[] scores = { 80, 90, 75 };

配列内のそれぞれの値を「要素」と呼びます。各要素には、位置を示す「インデックス」が割り当てられます。

C#の配列のインデックスは、0から始まります。

要素:          80  90  75
インデックス: 0 1 2

したがって、最初の要素を取得する場合は0を指定します。

C#
int firstScore = scores[0];

Console.WriteLine(firstScore);

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

80

配列の主な特徴

C#の配列には、次のような特徴があります。

  • 同じデータ型の値を複数格納できる

  • インデックスを使って要素へアクセスできる

  • インデックスは0から始まる

  • 配列を作成した後は要素数を変更できない

  • 配列はSystem.Arrayを継承する参照型である

  • 値型だけでなく、文字列や独自クラスのオブジェクトも格納できる

配列へ格納できるのは、基本的に宣言したデータ型と互換性のある値だけです。

C#
int[] numbers = { 10, 20, 30 };
string[] names = { "田中", "鈴木", "佐藤" };

int[]には整数、string[]には文字列を格納します。

配列を宣言する方法

C#で配列を宣言する基本構文は次のとおりです。

C#
データ型[] 変数名;

整数を格納する配列は、次のように宣言します。

C#
int[] numbers;

文字列を格納する配列の場合は、次のとおりです。

C#
string[] names;

この時点では、配列変数を宣言しただけであり、配列そのものは作成されていません。ローカル変数として宣言した場合、値を代入せずに使用することはできません。

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

new int[3]によって、3つの整数を格納できる配列が作成されます。

宣言と配列の作成を同時に行うこともできます。

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

配列を初期化する方法

配列を初期化する方法には、いくつかの書き方があります。

要素数を指定して初期化する

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

このコードでは、3つのint型データを格納できる配列を作成しています。

各要素には、データ型に応じた既定値が設定されます。int型の既定値は0です。

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

Console.WriteLine(numbers[0]);
Console.WriteLine(numbers[1]);
Console.WriteLine(numbers[2]);

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

0
0
0

主なデータ型の既定値は次のとおりです。

データ型既定値
int0
double0.0
boolfalse
char'\0'
参照型null

たとえば、stringは参照型なので、各要素の初期値はnullです。

C#
string[] names = new string[3];

初期値を指定して初期化する

配列の作成時に、格納する値を指定できます。

C#
int[] numbers = new int[] { 10, 20, 30 };

初期値から要素数を判断できるため、角括弧の中に要素数を書く必要はありません。

さらに、new int[]を省略して次のように記述できます。

C#
int[] numbers = { 10, 20, 30 };

C#では、この書き方がよく使われます。

文字列の配列も同様です。

C#
string[] fruits = { "りんご", "みかん", "ぶどう" };

型推論を利用して、次のように記述することもできます。

C#
var numbers = new[] { 10, 20, 30 };

この場合、右辺の値からint[]型と推論されます。

ただし、型推論を使う場合も、各要素に互換性のあるデータ型を指定する必要があります。

配列の要素を取得する方法

配列の要素を取得するには、配列名の後ろに角括弧を書き、その中へインデックスを指定します。

C#
string[] fruits = { "りんご", "みかん", "ぶどう" };

Console.WriteLine(fruits[0]);
Console.WriteLine(fruits[1]);
Console.WriteLine(fruits[2]);

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

りんご
みかん
ぶどう

配列の最後の要素を取得する場合は、Length - 1をインデックスとして指定できます。

C#
string lastFruit = fruits[fruits.Length - 1];

Console.WriteLine(lastFruit);

C#では、末尾から数えた位置を指定できるインデックス演算子^も利用できます。

C#
string lastFruit = fruits[^1];

Console.WriteLine(lastFruit);

^1は末尾から1番目、つまり最後の要素を表します。

配列の要素を変更する方法

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

C#
int[] numbers = { 10, 20, 30 };

numbers[1] = 100;

Console.WriteLine(numbers[1]);

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

100

変更後の配列は、次のようになります。

10, 100, 30

配列の要素数は変更できませんが、各要素の値は変更できます。

配列の要素数を取得する方法

配列の要素数を取得するには、Lengthプロパティを使用します。

C#
int[] numbers = { 10, 20, 30 };

Console.WriteLine(numbers.Length);

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

3

Lengthは要素数を返します。一方、インデックスは0から始まるため、最後の要素のインデックスはLength - 1です。

C#
int lastIndex = numbers.Length - 1;

Console.WriteLine(lastIndex);

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

2

配列をfor文で繰り返し処理する

配列の全要素を順番に処理したい場合は、for文を使用できます。

C#
int[] numbers = { 10, 20, 30 };

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

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

10
20
30

for文ではインデックスを利用できるため、要素の位置も必要な処理に向いています。

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

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

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

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

配列をforeach文で繰り返し処理する

インデックスが不要で、各要素を順番に取得するだけなら、foreach文が便利です。

C#
int[] numbers = { 10, 20, 30 };

foreach (int number in numbers)
{
Console.WriteLine(number);
}

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

10
20
30

型推論を使って、次のようにも記述できます。

C#
foreach (var number in numbers)
{
Console.WriteLine(number);
}

単純に全要素を処理する場合はforeach文、インデックスを使いたい場合や特定の範囲だけ処理したい場合はfor文が適しています。

配列に要素を追加する方法

C#の配列は、作成時に要素数が決まります。そのため、JavaScriptのpushのように、既存の配列へ直接要素を追加するメソッドはありません。

C#
int[] numbers = { 10, 20, 30 };

この配列の要素数は3で固定されています。4番目の要素を追加するには、新しい配列を作成する必要があります。

新しい配列を作成して要素を追加する

C#
int[] numbers = { 10, 20, 30 };
int[] newNumbers = new int[numbers.Length + 1];

for (int i = 0; i < numbers.Length; i++)
{
newNumbers[i] = numbers[i];
}

newNumbers[^1] = 40;
numbers = newNumbers;

これで、numbersの内容は次のようになります。

10, 20, 30, 40

ただし、この方法ではコピー処理を自分で記述する必要があります。

Array.Copyで要素をコピーする

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.Copyの主な引数は、コピー元の配列、コピー先の配列、コピーする要素数です。

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を実行すると、配列の作成とコピーが繰り返されるため、処理効率が低下する可能性があります。

LINQのAppendで要素を追加する

LINQのAppendを使い、新しい要素を末尾へ追加した配列を作成する方法もあります。

C#
using System.Linq;

int[] numbers = { 10, 20, 30 };

numbers = numbers.Append(40).ToArray();

Appendは元の配列を変更しません。新しい要素を含む列挙結果を作成し、ToArrayによって新しい配列へ変換しています。

複数の要素を追加する場合は、Concatを利用できます。

C#
using System.Linq;

int[] numbers = { 10, 20, 30 };
int[] additionalNumbers = { 40, 50 };

numbers = numbers.Concat(additionalNumbers).ToArray();

結果は次のとおりです。

10, 20, 30, 40, 50

AppendConcatは簡潔に記述できますが、新しい配列が作成される点には注意が必要です。

要素を頻繁に追加するならListを使う

要素の追加や削除を頻繁に行う場合は、配列よりもList<T>が適しています。

C#
using System.Collections.Generic;

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

numbers.Add(40);
numbers.Add(50);

コレクション式を利用できる環境では、次のように簡潔に初期化できます。

C#
List<int> numbers = [10, 20, 30];

numbers.Add(40);

List<T>Addメソッドを使えば、末尾へ簡単に要素を追加できます。

C#
foreach (int number in numbers)
{
Console.WriteLine(number);
}

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

10
20
30
40
50

指定した位置へ要素を追加する場合は、Insertメソッドを使用します。

C#
numbers.Insert(1, 15);

指定した値を削除する場合はRemove、インデックスを指定して削除する場合はRemoveAtを使用します。

C#
numbers.Remove(30);
numbers.RemoveAt(0);

配列とListの違い

C#の配列とList<T>は、どちらも複数の同じ型のデータを管理できますが、用途が異なります。

比較項目配列List<T>
要素数作成後は固定動的に増減できる
要素の追加新しい配列の作成が必要Addで追加できる
要素の削除新しい配列の作成が必要Removeなどで削除できる
要素数の取得LengthCount
インデックスアクセス可能可能
多次元構造多次元配列を利用できる入れ子で表現する
固定長データ適している利用可能
頻繁な追加・削除適していない適している

配列が向いているのは、次のようなケースです。

  • データ数があらかじめ決まっている

  • 作成後に要素数を変更しない

  • 固定された形式のデータを扱う

  • 多次元配列を使いたい

  • APIの引数や戻り値として配列が必要

List<T>が向いているのは、次のようなケースです。

  • データ数が事前に決まっていない

  • 要素を頻繁に追加・削除する

  • 可変長のコレクションを扱う

  • AddRemoveなどの便利な操作を使いたい

List<T>から配列へ変換する場合は、ToArrayメソッドを使用します。

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

int[] numbers = numberList.ToArray();

配列からList<T>へ変換する場合は、ToListメソッドを使用できます。

C#
using System.Linq;

int[] numbers = { 10, 20, 30 };

List<int> numberList = numbers.ToList();

多次元配列とは

C#では、表のように行と列を持つ多次元配列を作成できます。

2次元配列の基本構文は次のとおりです。

C#
データ型[,] 配列名;

たとえば、2行3列の整数配列は次のように作成します。

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

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

C#
int[,] numbers =
{
{ 10, 20, 30 },
{ 40, 50, 60 }
};

各要素へアクセスするには、行と列のインデックスを指定します。

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

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

10
60

多次元配列でも、インデックスは0から始まります。

numbers[0, 0] = 10
numbers[0, 1] = 20
numbers[0, 2] = 30
numbers[1, 0] = 40
numbers[1, 1] = 50
numbers[1, 2] = 60

多次元配列を繰り返し処理する

2次元配列を行と列に分けて処理するには、for文を入れ子にします。

C#
int[,] numbers =
{
{ 10, 20, 30 },
{ 40, 50, 60 }
};

for (int row = 0; row < numbers.GetLength(0); row++)
{
for (int column = 0; column < numbers.GetLength(1); column++)
{
Console.Write($"{numbers[row, column]} ");
}

Console.WriteLine();
}

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

10 20 30
40 50 60

GetLengthメソッドは、指定した次元の要素数を返します。

C#
int rowCount = numbers.GetLength(0);
int columnCount = numbers.GetLength(1);

GetLength(0)は1つ目の次元である行数、GetLength(1)は2つ目の次元である列数を表します。

全要素の合計数はLengthで取得できます。

C#
Console.WriteLine(numbers.Length);

2行3列の場合、実行結果は6です。

3次元配列

C#では、3次元以上の配列も作成できます。

C#
int[,,] data = new int[2, 3, 4];

要素へアクセスするには、3つのインデックスを指定します。

C#
data[0, 1, 2] = 100;

Console.WriteLine(data[0, 1, 2]);

次元数を取得するには、Rankプロパティを使用します。

C#
Console.WriteLine(data.Rank);

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

3

多次元配列は、座標、表形式データ、ゲームの盤面などを表現する場合に利用できます。ただし、次元が増えるほど処理を理解しにくくなるため、独自クラスや別のデータ構造を使ったほうがわかりやすい場合もあります。

ジャグ配列とは

ジャグ配列とは、配列の各要素として別の配列を格納する「配列の配列」です。

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

C#
データ型[][] 配列名;

たとえば、次のように作成します。

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

numbers[0] = new int[] { 10, 20 };
numbers[1] = new int[] { 30, 40, 50 };
numbers[2] = new int[] { 60 };

初期化をまとめて記述することもできます。

C#
int[][] numbers =
{
new int[] { 10, 20 },
new int[] { 30, 40, 50 },
new int[] { 60 }
};

コレクション式を利用できる環境では、次のようにも記述できます。

C#
int[][] numbers =
[
[10, 20],
[30, 40, 50],
[60]
];

要素へアクセスする場合は、角括弧を2回使用します。

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

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

20
50

ジャグ配列では、内部の配列ごとに異なる要素数を設定できます。

1行目: 2要素
2行目: 3要素
3行目: 1要素

これに対して、int[,]のような長方形の多次元配列では、すべての行が同じ列数を持ちます。

ジャグ配列を繰り返し処理する

C#
int[][] numbers =
{
new int[] { 10, 20 },
new int[] { 30, 40, 50 },
new int[] { 60 }
};

foreach (int[] row in numbers)
{
foreach (int number in row)
{
Console.Write($"{number} ");
}

Console.WriteLine();
}

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

10 20
30 40 50
60

各行の要素数が異なるデータを扱う場合は、長方形の多次元配列よりジャグ配列が適しています。

配列を並べ替える方法

配列を昇順に並べ替えるには、Array.Sortを使用します。

C#
int[] numbers = { 30, 10, 40, 20 };

Array.Sort(numbers);

foreach (int number in numbers)
{
Console.WriteLine(number);
}

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

10
20
30
40

降順にしたい場合は、昇順に並べ替えた後でArray.Reverseを使用できます。

C#
Array.Sort(numbers);
Array.Reverse(numbers);

LINQを使い、元の配列を変更せずに新しい配列を作成する方法もあります。

C#
using System.Linq;

int[] numbers = { 30, 10, 40, 20 };

int[] sortedNumbers = numbers.OrderBy(number => number).ToArray();
int[] descendingNumbers = numbers.OrderByDescending(number => number).ToArray();

Array.Sortは元の配列を直接変更しますが、OrderByは並べ替え結果から新しい配列を作成します。

配列から要素を検索する方法

指定した値のインデックスを検索するには、Array.IndexOfを使用します。

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

int index = Array.IndexOf(names, "鈴木");

Console.WriteLine(index);

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

1

値が見つからなかった場合は、-1が返されます。

C#
int index = Array.IndexOf(names, "山田");

if (index == -1)
{
Console.WriteLine("該当する要素はありません");
}

条件に一致する要素を検索する場合は、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);

配列をコピーする方法

配列は参照型です。そのため、単純に別の変数へ代入した場合、配列そのものはコピーされません。

C#
int[] original = { 10, 20, 30 };
int[] copied = original;

copied[0] = 100;

Console.WriteLine(original[0]);

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

100

originalcopiedが同じ配列を参照しているため、一方で要素を変更すると、もう一方から見た値も変わります。

別の配列としてコピーするには、Cloneを使用できます。

C#
int[] original = { 10, 20, 30 };
int[] copied = (int[])original.Clone();

copied[0] = 100;

Console.WriteLine(original[0]);
Console.WriteLine(copied[0]);

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

10
100

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

C#
using System.Linq;

int[] copied = original.ToArray();

ただし、要素が参照型の場合、CloneToArrayで作成されるのは浅いコピーです。配列自体は別になりますが、各要素が参照するオブジェクトまでは複製されません。

配列の範囲を切り出す方法

C#の範囲演算子..を使うと、配列の一部を切り出して新しい配列を作成できます。

C#
int[] numbers = { 10, 20, 30, 40, 50 };

int[] selected = numbers[1..4];

1..4は、インデックス1以上、4未満の範囲を表します。

selectedの内容は次のとおりです。

20, 30, 40

先頭から指定位置までを取得する場合は、開始位置を省略できます。

C#
int[] firstThree = numbers[..3];

指定位置から末尾までを取得する場合は、終了位置を省略します。

C#
int[] fromThird = numbers[2..];

末尾からの位置を表す^と組み合わせることもできます。

C#
int[] withoutLast = numbers[..^1];

このコードでは、最後の要素を除いた新しい配列が作成されます。

配列で発生しやすいエラー

配列を扱うときに特に注意したいのが、存在しないインデックスへのアクセスです。

C#
int[] numbers = { 10, 20, 30 };

Console.WriteLine(numbers[3]);

この配列で利用できるインデックスは012です。3を指定すると、IndexOutOfRangeExceptionが発生します。

配列を繰り返し処理するときは、条件式を次のように記述します。

C#
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}

次のように<=を使うと、最後にnumbers[numbers.Length]へアクセスしてしまいます。

C#
for (int i = 0; i <= numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}

配列の最後の有効なインデックスは、常にLength - 1です。

また、配列変数がnullの状態でアクセスすると、NullReferenceExceptionが発生します。

C#
int[]? numbers = null;

Console.WriteLine(numbers.Length);

利用前にnullではないことを確認するか、配列を適切に初期化する必要があります。

C#
int[] numbers = Array.Empty<int>();

Array.Empty<T>()を使うと、要素数が0の配列を取得できます。

C#
Console.WriteLine(numbers.Length);

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

0

nullと空の配列は意味が異なります。nullは配列が存在しない状態、空の配列は配列が存在するものの要素が1つもない状態です。

まとめ

C#の配列(array)は、同じデータ型の値を複数まとめて管理するための基本的なデータ構造です。

配列は、次のように宣言・初期化できます。

C#
int[] numbers = { 10, 20, 30 };

要素へアクセスするときは、0から始まるインデックスを指定します。

C#
Console.WriteLine(numbers[0]);

要素数はLengthプロパティで取得できます。

C#
Console.WriteLine(numbers.Length);

配列は作成後に要素数を変更できません。要素を追加する場合は、Array.ResizeやLINQのAppendなどを使って新しい配列を作成します。

C#
Array.Resize(ref numbers, numbers.Length + 1);
numbers[^1] = 40;

ただし、要素を頻繁に追加・削除する場合は、List<T>を使うほうが適しています。

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

行と列が固定された表形式のデータには多次元配列、行ごとに要素数が異なるデータにはジャグ配列を利用できます。

C#
int[,] matrix =
{
{ 10, 20 },
{ 30, 40 }
};

int[][] jagged =
{
new int[] { 10, 20 },
new int[] { 30, 40, 50 }
};

データ数が固定されている場合は配列、データ数が変化する場合はList<T>というように、目的に応じて使い分けることが大切です。