C# Array.Resizeの使い方|配列サイズ変更の基本・注意点・Listとの違いを解説
はじめに
C#で配列を扱っていると、「あとから要素数を増やしたい」「不要な要素を削って配列サイズを小さくしたい」という場面があります。
しかし、C#の配列は本来、作成したあとにサイズを変更できない固定長のデータ構造です。そこで使えるのがArray.Resizeメソッドです。
Array.Resizeを使うと、既存の配列をもとにして、要素数の異なる新しい配列へ変更できます。ただし、内部的には新しい配列を作り直しているため、使い方や注意点を理解しておく必要があります。
この記事では、c# array.resizeの基本的な使い方から、配列を拡張・縮小するサンプルコード、List<T>との違い、よくあるエラーまでわかりやすく解説します。
1. C#のArray.Resizeとは?配列サイズ変更の基本
1-1. Array.Resizeでできること
Array.Resizeは、C#で配列のサイズを変更したいときに使うメソッドです。
たとえば、次のような処理ができます。
C#int[] numbers = { 1, 2, 3 };
Array.Resize(ref numbers, 5);
Console.WriteLine(numbers.Length); // 5
この例では、要素数3のint配列を、要素数5の配列に変更しています。
もとの要素である1, 2, 3は保持され、追加された部分にはint型の初期値である0が入ります。
1-2. 配列は本来「固定長」である理由
C#の配列は、作成時に要素数が決まります。
C#int[] numbers = new int[3];
このように作成した配列は、基本的に長さを直接変更できません。
配列はメモリ上に連続した領域として確保されるため、途中でサイズを増やすには、十分な連続領域を再確保する必要があります。そのため、C#の配列は固定長として扱われます。
1-3. Array.Resizeは既存配列を直接伸ばすわけではない
Array.Resizeという名前を見ると、既存の配列そのものを直接伸ばしているように見えるかもしれません。
しかし実際には、内部で新しいサイズの配列を作成し、元の要素をコピーして、新しい配列を参照するように変更しています。
つまり、次のような処理をまとめて行っているイメージです。
C#int[] oldArray = { 1, 2, 3 };
int[] newArray = new int[5];
Array.Copy(oldArray, newArray, oldArray.Length);
oldArray = newArray;
Array.Resizeは便利ですが、裏側では新しい配列の作成とコピーが行われる点を理解しておきましょう。
1-4. Array.Resizeを使う主なケース
Array.Resizeは、次のような場面で使われます。
配列の末尾に要素を追加したい場合や、不要な要素を削除して配列サイズを小さくしたい場合に便利です。
また、メソッドの戻り値として配列が必要な場合や、外部ライブラリとの都合でList<T>ではなく配列を扱う必要がある場合にも使われます。
ただし、要素数が頻繁に変わる処理では、Array.ResizeよりもList<T>を使うほうが適していることが多いです。
2. Array.Resizeの基本的な使い方
2-1. 基本構文と引数の意味
Array.Resizeの基本構文は次のとおりです。
C#Array.Resize(ref 配列変数, 新しいサイズ);
第1引数には、サイズを変更したい配列をref付きで渡します。
第2引数には、新しい配列の要素数を指定します。
C#Array.Resize(ref numbers, 10);
この場合、numbers配列のサイズを10に変更します。
2-2. 配列を拡張するサンプルコード
配列のサイズを大きくする例を見てみましょう。
C#int[] numbers = { 10, 20, 30 };
Array.Resize(ref numbers, 5);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のようになります。
C#10
20
30
0
0
もとの要素は保持され、追加された要素にはint型の初期値である0が入ります。
2-3. 配列を縮小するサンプルコード
配列のサイズを小さくすることもできます。
C#int[] numbers = { 10, 20, 30, 40, 50 };
Array.Resize(ref numbers, 3);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#10
20
30
サイズを小さくすると、新しいサイズに収まらない要素は削除されます。
この例では、40と50は失われます。
2-4. 文字列配列・int配列での使用例
Array.Resizeは、int配列だけでなく、文字列配列にも使えます。
C#string[] names = { "Alice", "Bob" };
Array.Resize(ref names, 4);
names[2] = "Charlie";
names[3] = "David";
foreach (string name in names)
{
Console.WriteLine(name);
}
実行結果は次のようになります。
C#Alice
Bob
Charlie
David
string型の配列を拡張した場合、追加された要素の初期値はnullです。
C#string[] names = { "Alice", "Bob" };
Array.Resize(ref names, 3);
Console.WriteLine(names[2] == null); // True
2-5. Resize後の要素の初期値
Array.Resizeで配列を拡張した場合、追加された部分には型ごとの初期値が入ります。
intなら0、boolならfalse、stringやクラス型ならnullです。
C#int[] numbers = { 1, 2 };
Array.Resize(ref numbers, 4);
Console.WriteLine(numbers[2]); // 0
Console.WriteLine(numbers[3]); // 0
C#string[] texts = { "A", "B" };
Array.Resize(ref texts, 4);
Console.WriteLine(texts[2] == null); // True
値型と参照型で初期値が異なる点に注意しましょう。
3. Array.Resizeを使うときの重要な注意点
3-1. refキーワードが必要な理由
Array.Resizeでは、配列をref付きで渡す必要があります。
C#Array.Resize(ref numbers, 5);
これは、Array.Resizeが内部で新しい配列を作成し、配列変数の参照先を変更するためです。
refを付けないと、呼び出し元の配列変数を書き換えられません。
次のように書くとコンパイルエラーになります。
C#Array.Resize(numbers, 5); // エラー
必ずrefを付けて呼び出しましょう。
3-2. 元の配列参照が変更される仕組み
Array.Resizeを実行すると、配列変数は新しい配列を参照するようになります。
C#int[] numbers = { 1, 2, 3 };
int[] oldReference = numbers;
Array.Resize(ref numbers, 5);
Console.WriteLine(oldReference.Length); // 3
Console.WriteLine(numbers.Length); // 5
この例では、oldReferenceは古い配列を参照したままです。
一方、numbersは新しく作られた要素数5の配列を参照しています。
つまり、Array.Resizeは既存配列そのものを変形するのではなく、新しい配列への参照に差し替える処理です。
3-3. null配列に対してResizeした場合
Array.Resizeは、nullの配列に対しても使えます。
C#int[] numbers = null;
Array.Resize(ref numbers, 3);
Console.WriteLine(numbers.Length); // 3
この場合、新しいサイズの配列が作成されます。
要素には型ごとの初期値が入ります。
C#foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#0
0
0
3-4. サイズを小さくすると削除される要素
配列を縮小すると、新しいサイズより後ろにある要素は削除されます。
C#string[] fruits = { "Apple", "Banana", "Orange", "Grape" };
Array.Resize(ref fruits, 2);
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
実行結果は次のようになります。
C#Apple
Banana
OrangeとGrapeは失われます。
一度縮小して削除された要素は、あとから再拡張しても戻りません。
C#Array.Resize(ref fruits, 4);
Console.WriteLine(fruits[2] == null); // True
3-5. 多次元配列には使えない点
Array.Resizeは、基本的に1次元配列を対象としたメソッドです。
次のような2次元配列には使えません。
C#int[,] matrix = new int[2, 3];
// Array.Resize(ref matrix, 10); // エラー
2次元配列のサイズを変更したい場合は、新しい2次元配列を作成し、必要な要素を手動でコピーする必要があります。
C#int[,] oldMatrix = new int[2, 2]
{
{ 1, 2 },
{ 3, 4 }
};
int[,] newMatrix = new int[3, 3];
for (int i = 0; i < oldMatrix.GetLength(0); i++)
{
for (int j = 0; j < oldMatrix.GetLength(1); j++)
{
newMatrix[i, j] = oldMatrix[i, j];
}
}
3-6. 頻繁なResizeはパフォーマンスに注意
Array.Resizeは、内部で新しい配列を作成し、元の要素をコピーします。
そのため、何度も繰り返し呼び出すとパフォーマンスが低下しやすくなります。
たとえば、次のようなコードは効率がよくありません。
C#int[] numbers = new int[0];
for (int i = 0; i < 10000; i++)
{
Array.Resize(ref numbers, numbers.Length + 1);
numbers[numbers.Length - 1] = i;
}
このコードでは、ループのたびに新しい配列が作成され、コピー処理が発生します。
要素数が増減する処理では、基本的にList<T>を使うほうが適しています。
4. Array.Resizeの実践例
4-1. 配列の末尾に要素を追加する
Array.Resizeを使えば、配列の末尾に要素を追加できます。
C#int[] numbers = { 1, 2, 3 };
Array.Resize(ref numbers, numbers.Length + 1);
numbers[numbers.Length - 1] = 4;
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#1
2
3
4
配列サイズを1つ増やしてから、最後の位置に新しい値を代入しています。
4-2. 配列から不要な要素を削除する
末尾の要素を削除する場合は、配列サイズを1つ小さくします。
C#string[] names = { "Alice", "Bob", "Charlie" };
Array.Resize(ref names, names.Length - 1);
foreach (string name in names)
{
Console.WriteLine(name);
}
実行結果は次のようになります。
C#Alice
Bob
末尾のCharlieが削除されました。
途中の要素を削除したい場合は、Whereなどを使うと簡単です。
C#int[] numbers = { 1, 2, 3, 4, 5 };
numbers = numbers.Where(n => n != 3).ToArray();
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#1
2
4
5
4-3. ユーザー入力に応じて配列サイズを変更する
ユーザー入力を受け取りながら配列に追加する例です。
C#string[] inputs = new string[0];
while (true)
{
Console.Write("文字を入力してください。終了する場合はend:");
string input = Console.ReadLine();
if (input == "end")
{
break;
}
Array.Resize(ref inputs, inputs.Length + 1);
inputs[inputs.Length - 1] = input;
}
Console.WriteLine("入力された値:");
foreach (string item in inputs)
{
Console.WriteLine(item);
}
このコードでは、入力のたびに配列サイズを1つ増やしています。
ただし、入力回数が多くなる場合はList<string>を使うほうが効率的です。
4-4. メソッド内でArray.Resizeを使う例
メソッド内で配列サイズを変更する場合も、refが重要です。
C#static void AddNumber(ref int[] numbers, int value)
{
Array.Resize(ref numbers, numbers.Length + 1);
numbers[numbers.Length - 1] = value;
}
呼び出し側は次のように書きます。
C#int[] numbers = { 1, 2, 3 };
AddNumber(ref numbers, 4);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#1
2
3
4
メソッド内で配列の参照先を変更するため、引数にもrefが必要です。
4-5. よくあるコンパイルエラーと対処法
Array.Resizeでよくあるエラーの1つが、refを付け忘れることです。
C#int[] numbers = { 1, 2, 3 };
Array.Resize(numbers, 5); // エラー
正しくは次のように書きます。
C#Array.Resize(ref numbers, 5);
また、プロパティやメソッドの戻り値をそのままrefに渡すこともできません。
C#// Array.Resize(ref GetArray(), 5); // エラー
refに渡せるのは、書き換え可能な変数です。
いったん変数に代入してから使いましょう。
C#int[] numbers = GetArray();
Array.Resize(ref numbers, 5);
5. Array.ResizeとList<T>の違い
5-1. 配列とList<T>の基本的な違い
配列とList<T>は、どちらも複数の値をまとめて扱うための仕組みです。
配列は固定長で、作成後に要素数を直接変更できません。
C#int[] numbers = new int[3];
一方、List<T>は可変長のコレクションです。
C#List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
要素の追加や削除を簡単に行えるのがList<T>の特徴です。
5-2. 要素数が変わるならList<T>が向いている理由
要素数が頻繁に変わる処理では、List<T>のほうが向いています。
C#List<int> numbers = new List<int>();
for (int i = 0; i < 10000; i++)
{
numbers.Add(i);
}
List<T>は内部で容量を管理しており、毎回必ず全要素をコピーするわけではありません。
そのため、Array.Resizeを何度も呼び出すより効率的です。
また、要素の追加にはAdd、削除にはRemoveやRemoveAtが使えるため、コードも読みやすくなります。
5-3. Array.Resizeが向いているケース
Array.Resizeが向いているのは、配列として扱う必要がある場合です。
たとえば、APIやライブラリが配列を要求している場合、最終的なデータ形式を配列にしたい場合などです。
また、サイズ変更の回数が少ない場合であれば、Array.Resizeを使っても大きな問題になりにくいです。
C#int[] values = { 1, 2, 3 };
Array.Resize(ref values, 4);
values[3] = 4;
単発のサイズ変更であれば、Array.Resizeはシンプルで使いやすい方法です。
5-4. List<T>から配列へ変換する方法
List<T>から配列へ変換するには、ToArrayメソッドを使います。
C#List<int> list = new List<int> { 1, 2, 3 };
int[] array = list.ToArray();
要素の追加や削除はList<T>で行い、最後に配列へ変換する方法はよく使われます。
C#List<string> names = new List<string>();
names.Add("Alice");
names.Add("Bob");
string[] nameArray = names.ToArray();
5-5. 配列からList<T>へ変換する方法
配列からList<T>へ変換するには、コンストラクタを使います。
C#int[] array = { 1, 2, 3 };
List<int> list = new List<int>(array);
LINQのToListを使う方法もあります。
C#int[] array = { 1, 2, 3 };
List<int> list = array.ToList();
ToListを使う場合は、次の名前空間が必要です。
C#using System.Linq;
6. Array.Resizeの代替方法
6-1. List<T>を使う方法
配列サイズを頻繁に変更するなら、List<T>を使うのが基本です。
C#List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
numbers.Remove(20);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
List<T>は、要素の追加・削除・検索などがしやすく、可変長データを扱う場合に便利です。
最終的に配列が必要な場合は、ToArrayで変換できます。
C#int[] array = numbers.ToArray();
6-2. Array.Copyを使う方法
Array.Resizeを使わずに、Array.Copyで新しい配列へコピーする方法もあります。
C#int[] oldArray = { 1, 2, 3 };
int[] newArray = new int[5];
Array.Copy(oldArray, newArray, oldArray.Length);
newArray[3] = 4;
newArray[4] = 5;
Array.Resizeの内部処理に近い書き方です。
コピーする範囲を細かく制御したい場合には、Array.Copyが便利です。
6-3. LINQで配列を加工する方法
配列から条件に合う要素だけを取り出したい場合は、LINQを使う方法もあります。
C#int[] numbers = { 1, 2, 3, 4, 5 };
int[] evenNumbers = numbers.Where(n => n % 2 == 0).ToArray();
foreach (int number in evenNumbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#2
4
LINQを使うと、削除や抽出の処理を簡潔に書けます。
ただし、新しい配列を作成する点はArray.Resizeと同じです。
6-4. Span<T>やMemory<T>との違い
Span<T>やMemory<T>は、配列やメモリ領域の一部を効率よく扱うための型です。
ただし、Span<T>やMemory<T>自体が配列サイズを変更するわけではありません。
C#int[] numbers = { 1, 2, 3, 4, 5 };
Span<int> span = numbers.AsSpan(1, 3);
foreach (int number in span)
{
Console.WriteLine(number);
}
この例では、配列の一部である2, 3, 4を参照しています。
サイズ変更ではなく、既存データの一部を効率的に扱いたい場合に使います。
6-5. 目的別の使い分け早見表
配列サイズを1回だけ変更したい場合は、Array.Resizeが簡単です。
要素を何度も追加・削除する場合は、List<T>が向いています。
コピー範囲を細かく制御したい場合は、Array.Copyを使うとよいでしょう。
条件に合う要素だけを抽出したい場合は、LINQが便利です。
既存配列の一部を効率よく参照したい場合は、Span<T>やMemory<T>が選択肢になります。
7. Array.Resizeでよくある疑問
7-1. Array.Resizeは元の配列を上書きする?
Array.Resizeは、元の配列そのものを直接上書きするわけではありません。
内部では新しい配列を作成し、元の要素をコピーして、配列変数の参照先を変更します。
C#int[] numbers = { 1, 2, 3 };
Array.Resize(ref numbers, 5);
この処理のあと、numbersは新しい配列を参照しています。
7-2. Resize後に元の要素は保持される?
配列を拡張した場合、元の要素は保持されます。
C#int[] numbers = { 1, 2, 3 };
Array.Resize(ref numbers, 5);
Console.WriteLine(numbers[0]); // 1
Console.WriteLine(numbers[1]); // 2
Console.WriteLine(numbers[2]); // 3
ただし、配列を縮小した場合、新しいサイズに収まらない要素は削除されます。
C#Array.Resize(ref numbers, 2);
Console.WriteLine(numbers.Length); // 2
この場合、3番目以降の要素は失われます。
7-3. 配列の途中に要素を追加できる?
Array.Resizeだけでは、配列の途中に要素を挿入することはできません。
途中に追加したい場合は、サイズを増やしたあと、要素を後ろにずらす必要があります。
C#int[] numbers = { 1, 2, 4 };
int index = 2;
int value = 3;
Array.Resize(ref numbers, numbers.Length + 1);
for (int i = numbers.Length - 1; i > index; i--)
{
numbers[i] = numbers[i - 1];
}
numbers[index] = value;
foreach (int number in numbers)
{
Console.WriteLine(number);
}
実行結果は次のとおりです。
C#1
2
3
4
ただし、このような処理が多い場合は、List<T>のInsertを使うほうが簡単です。
C#List<int> numbers = new List<int> { 1, 2, 4 };
numbers.Insert(2, 3);
7-4. 2次元配列のサイズ変更はできる?
Array.Resizeでは、2次元配列のサイズ変更はできません。
2次元配列を拡張・縮小したい場合は、新しい2次元配列を作成して、必要な要素をコピーします。
C#int[,] oldArray = new int[2, 2]
{
{ 1, 2 },
{ 3, 4 }
};
int[,] newArray = new int[3, 3];
for (int i = 0; i < oldArray.GetLength(0); i++)
{
for (int j = 0; j < oldArray.GetLength(1); j++)
{
newArray[i, j] = oldArray[i, j];
}
}
2次元以上のデータ構造でサイズ変更が多い場合は、List<List<T>>などの利用も検討するとよいでしょう。
7-5. Array.Resizeとnew配列作成は何が違う?
newで配列を作成すると、まったく新しい空の配列が作られます。
C#int[] numbers = new int[5];
この場合、要素はすべて初期値になります。
一方、Array.Resizeは元の配列の要素を可能な範囲でコピーします。
C#int[] numbers = { 1, 2, 3 };
Array.Resize(ref numbers, 5);
この場合、1, 2, 3は保持され、追加された部分には初期値が入ります。
つまり、元の要素を保持しながらサイズ変更したい場合はArray.Resize、完全に新しい配列を作りたい場合はnewを使います。
まとめ
C#のArray.Resizeは、配列のサイズを変更したいときに使える便利なメソッドです。
配列を拡張すると元の要素は保持され、追加部分には型ごとの初期値が入ります。配列を縮小すると、新しいサイズに収まらない要素は削除されます。
ただし、Array.Resizeは既存の配列を直接伸ばしているわけではありません。内部では新しい配列を作成し、元の要素をコピーして、配列変数の参照先を変更しています。そのため、refキーワードが必要です。
サイズ変更が1回だけ、または少数回であればArray.Resizeは使いやすい方法です。一方で、要素数が頻繁に変わる処理では、List<T>を使うほうが効率的でコードも簡潔になります。
配列として扱う必要がある場合はArray.Resize、可変長のデータを扱う場合はList<T>というように、目的に応じて使い分けましょう。

