Menu

C#の配列:宣言、Length、ソート、2次元配列とジャグ配列

C#の配列は、1つの型の要素を固定の数だけ持つブロックです。配列の宣言と初期化、Lengthの読み取り、Arrayクラスによるソートと検索、安全なコピー、そして2次元配列とジャグ配列の扱い方を学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

配列は、1つの型の要素を固定の数だけ、メモリ上に隣り合わせで保持します。長さは作成時に決め、要素は0から始まるインデックスで読み書きし、その後に長さが変わることはありません。

配列の宣言と初期化

配列の型は、要素の型の後に [] を付けたものです。作り方はいくつかあります。

出力:

90, 0, 0, 72
Ben
2 prices, 3 days

new int[4] は、すべての要素をその型の既定値で埋めます。数値なら 0、bool なら false、文字列やその他の参照型なら null です。短い形 { "Ana", "Ben" } は宣言でしか使えません。既存の変数に代入するときは names = new[] { "Dev", "Eli" }; が必要です。

Length、インデックス、最後の要素

インデックスは 0 から Length - 1 までです。Length はプロパティなので、(LINQの Count() メソッドと違って)かっこは付けません。

出力:

Count: 5
First: 18
Last: 16
Caught IndexOutOfRangeException

範囲外を読み書きすると、実行時に IndexOutOfRangeException が投げられます。コンパイラはこれを検出しません。よくある原因は、ループの条件を i < arr.Length ではなく i <= arr.Length と書くことです。

C# 8以降では末尾からのインデックスと範囲の構文が加わり、最後の要素やスライスを短く書けます。

int last = temps[^1];        // 16
int[] middle = temps[1..4];  // { 21, 25, 19 }, a new array

配列をループする

for ループならインデックスが得られます。foreach は各要素を返し、読むだけならこちらのほうが明確です。

出力:

4.95 | 13.20 | 3.58
Total: 21.73

foreach の変数は読み取り専用で、ループの中で p = 0; と書くとコンパイルエラーになります。要素を変更するには、インデックスでループします。

ソート、反転、検索

静的な Array クラスにヘルパーがあります。Array.Sort はその場で並べ替えて何も返さないので、元の順序は失われます。

出力:

Ascending:  60, 72, 79, 88, 95
Descending: 95, 88, 79, 72, 60
Index of 60: 4
Index of 99: -1
Any above 90: True
First below 75: 72
Ana, ben, chloe
fig, kiwi, banana

Array.IndexOf は値が見つからないと -1 を返します。Array.Find は何も一致しないとその型の既定値(ここでは 0)を返し、数値では区別がつかなくなります。それが問題になるときは Array.FindIndex を使います。1回の呼び出しで降順にソートするには Array.Sort(scores, (a, b) => b.CompareTo(a)) と書きます。

元の配列に触れずにソート結果が欲しいなら、LINQが新しい配列を返します:var sorted = scores.OrderBy(s => s).ToArray();。LINQを参照してください。

サイズの変更:配列は大きくならない

長さは固定です。Array.Resize は指定したサイズの新しい配列を作り、要素をコピーし、ref で渡した変数を更新します。

出力:

4
2
True
False

サイズを変えるたびにすべての要素がコピーされます。ループの中で配列を1要素ずつ大きくするのは遅く、List<T> は内部の配列を2倍にすることで、まさにそれをより効率的に行ってくれます。

配列は参照型:コピー

配列を別の変数に代入すると、要素ではなく参照がコピーされます。すると両方の名前が同じ配列を指します。

出力:

100
2
0, 100, 2, 3, 0

配列をメソッドに渡すときも同じで、メソッドは呼び出し側の要素を変更できます。Clone、Array.Copy、CopyTo、LINQの ToArray() はどれも浅いコピーを作ります。オブジェクトの配列では、新しい配列も同じオブジェクトへの参照を持つので、copy[0].Name を変更すると original[0] から見える内容も変わります。

配列を == で比較すると参照が比較されます。内容を比較するには、System.Linq の a.SequenceEqual(b) を使います。

2次元配列(多次元配列)

長方形のグリッドは、角かっこの中にカンマを入れて宣言します。[row, col] でアクセスします。

出力:

Rows: 2
Cols: 3
Length: 6, Rank: 2
. X . 
. . X 

2次元配列の Length は要素の総数(2行3列のグリッドなら6)なので、ネストしたループでは GetLength(0) と GetLength(1) を使います。Rank は次元の数です。3次元も同じように new int[4, 4, 4] と cube[x, y, z] で扱えます。2次元配列を foreach すると、すべての要素を行ごとに訪れます。

ジャグ配列(配列の配列)

ジャグ配列 int[][] は、要素そのものが配列である配列で、内側の各配列は独自の長さを持てます。「顧客ごとの注文」のように行ごとにサイズが異なるデータに向いています。

出力:

Customer 0: 2 orders, 20, 35
Customer 1: 1 orders, 12
Customer 2: 4 orders, 8, 15, 40, 22
True

new int[3][] が作るのは外側の配列だけです。各行は null から始まり、行を代入する前に orders[0][0] にアクセスすると NullReferenceException が投げられます。new int[3][4] という構文は許されていないので、各行は自分で作ります。

本当のグリッド(盤面、行列、ピクセル)には [,] を、行の長さが異なる場合や行全体を入れ替えたい場合には [][] を使います。

配列とListの比較

配列 T[]List<T>
サイズ作成時に固定増減する
要素数Length プロパティCount プロパティ
追加と削除不可(Array.Resize のみ)Add、Insert、Remove
多次元[,] と [][]リストのリストのみ
典型的な用途サイズが決まっているもの、参照表、バッファー変化するコレクション

変換はどちらの方向も1回の呼び出しで済みます:new List<int>(arr) または arr.ToList()、そして list.ToArray()。List<T> は伸縮できる版です。

よくある間違い

  • ループでの i <= arr.Length。 最後の有効なインデックスは Length - 1 なので、IndexOutOfRangeException が投げられます。
  • 配列をそのまま表示する。 Console.WriteLine(arr) は System.Int32[] と表示します。string.Join を使います。
  • Array.Sort がソート済みの配列を返すと思う。 void を返し、その場で並べ替えます。
  • = でコピーする。 それは参照のコピーです。Clone() か ToArray() を使います。
  • 作成前のジャグ配列の行を使う。 new T[n][] の各行は、代入されるまで null です。
  • 2次元配列の行数に Length を使う。 すべての要素を数えてしまいます。GetLength(0) を使います。

よくある質問

C#で配列の長さを取得するには?

Length プロパティを使います:scores.Length。メソッドではなくプロパティなので、かっこは付けません。2次元配列の Length は要素の総数です。行数には GetLength(0)、列数には GetLength(1) を使います。

C#で配列をソートするには?

Array.Sort(arr) は配列をその場で昇順に並べ替えます。降順にするには、ソートの後に Array.Reverse(arr) を呼ぶか、比較を渡します:Array.Sort(arr, (a, b) => b.CompareTo(a))。元の配列を残したまま並べ替えたコピーを得るには、LINQを使います:arr.OrderBy(x => x).ToArray()。

C#の2次元配列とジャグ配列の違いは何ですか?

2次元配列 int[,] は1つの長方形のブロックで、すべての行が同じ列数を持ち、grid[row, col] でアクセスします。ジャグ配列 int[][] は配列の配列で、各行は独自の長さを持てる別々の配列であり、rows[i][j] でアクセスします。ジャグ配列の各行は使う前に作成する必要があり、そうしないと null です。

C#で配列のサイズを変更できますか?

できません。配列の長さは作成時に固定されます。Array.Resize(ref arr, newSize) はサイズを変えているように見えますが、新しい配列を確保して要素をコピーし、変数を新しい配列に向けています。サイズが頻繁に変わるなら List<T> を使います。

配列を表示するとSystem.Int32[]と出るのはなぜですか?

Console.WriteLine(arr) は arr.ToString() を呼びますが、配列はこれをオーバーライドしていないので、型名が表示されます。代わりに要素を1つの文字列に連結します:Console.WriteLine(string.Join(", ", arr))。

C#では配列とListのどちらを使うべきですか?

12か月やゲームの盤面のように、要素の数がわかっていて固定なら配列を使います。時間とともに要素が追加や削除されるなら List<T> を使います。どちらも定数時間でインデックスアクセスでき、List<T> は内部で要素を配列に格納しています。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める