C#プログラミングにおいて、複数のデータを格子状や表形式で管理したい場合に欠かせないのが「2次元配列」です。

C#には、行と列が固定された(矩形配列)としての多次元配列と、各行の長さが異なる(配列の配列)であるジャグ配列の2種類が存在します。

これらは似て非なるものであり、初期化の方法やメモリ構造、パフォーマンス特性が大きく異なります。

本記事では、初心者から中級者までを対象に、C#で2次元配列を初期化するあらゆるパターンを網羅的に解説します。

多次元配列とジャグ配列の構文の違いはもちろん、最新のC#における簡略化された記述法や、実務で役立つ使い分けの基準についても詳しく見ていきましょう。

この記事を読めば、用途に合わせた最適な配列の選択と初期化ができるようになるはずです。

C#における2次元配列の2つの形式

C#で2次元のデータを扱う際、まず理解しなければならないのが「多次元配列」と「ジャグ配列」の決定的な違いです。

これらを混同すると、初期化時にコンパイルエラーが発生したり、意図しない挙動に悩まされたりすることになります。

多次元配列(矩形配列)とは

多次元配列は、すべての行が同じ数の要素を持つ「長方形」の形をした配列です。

メモリ上では連続した領域に配置されるため、数学的な行列計算や、データ構造が固定されているグリッド情報の管理に適しています。

型宣言には int[,] のようにカンマを使用するのが特徴です。

ジャグ配列(Jagged Array)とは

ジャグ配列は「配列を要素として持つ配列」です。

各行(各要素の配列)が異なる長さを持つことができるため、「ギザギザな(Jagged)」配列と呼ばれます。

型宣言には int[][] のように、ブラケットを重ねて記述します。

柔軟性が高く、多くのデータ処理シーンで利用されます。

多次元配列(矩形配列)の初期化パターン

まずは、規則正しい行列構造を持つ多次元配列の初期化方法を解説します。

多次元配列は宣言時にすべての次元の要素数を確定させるのが基本です。

サイズを指定して初期化する

最も基本的な方法は、new キーワードを使用して行と列の数を明示的に指定する方法です。

C#
// 3行2列のint型2次元配列を宣言・初期化
int[,] matrix = new int[3, 2];

// 初期化直後は、型に応じたデフォルト値(intなら0)が格納されています
matrix[0, 0] = 10;
matrix[0, 1] = 20;

宣言と同時に値を代入する

宣言と同時に具体的な値をセットする場合は、中括弧 { } をネストさせて記述します。

この場合、要素数からサイズが自動的に推論されるため、サイズの数値指定を省略できます。

C#
// 値を指定して初期化(サイズ明記あり)
int[,] data1 = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };

// サイズ指定を省略(推奨される書き方)
int[,] data2 = new int[,] { { 10, 20 }, { 30, 40 }, { 50, 60 } };

// さらに簡略化した書き方(型が明確な場合)
int[,] data3 = { { 100, 200 }, { 300, 400 } };

実行結果の確認

多次元配列の内容を確認するためのコードと、その出力を示します。

C#
using System;

class Program
{
    static void Main()
    {
        // 2行3列の配列を初期化
        int[,] table = { { 1, 2, 3 }, { 4, 5, 6 } };

        Console.WriteLine("多次元配列の内容:");
        for (int i = 0; i < table.GetLength(0); i++) // 行のループ
        {
            for (int j = 0; j < table.GetLength(1); j++) // 列のループ
            {
                Console.Write(table[i, j] + " ");
            }
            Console.WriteLine();
        }
    }
}
実行結果
多次元配列の内容:
1 2 3 
4 5 6

注意点として、多次元配列では GetLength(0) で行数を、GetLength(1) で列数を取得します。

Length プロパティを使用すると全要素の合計(この場合は6)が返ってくるため、ループ処理時には注意が必要です。

ジャグ配列(配列の配列)の初期化パターン

ジャグ配列は、外側の配列の中に内側の配列が格納されている構造です。

そのため、初期化も2段階で行う必要があるという特徴があります。

ステップに分けた初期化

ジャグ配列では、まず「行数」だけを指定して外側の配列を作成し、その後に各行に対して「列数」の異なる配列を割り当てます。

C#
// まず「3つの配列を格納できる配列」を宣言
int[][] jagged = new int[3][];

// 各行に異なる長さの配列を代入
jagged[0] = new int[2]; // 0行目は2要素
jagged[1] = new int[4]; // 1行目は4要素
jagged[2] = new int[3]; // 2行目は3要素

宣言と同時に値を代入する

ジャグ配列も、宣言時に一括で値を代入することが可能です。

ただし、各要素が独立した配列であるため、要素ごとに new int[] を記述する必要があります。

C#
// ジャグ配列の一括初期化
int[][] numbers = new int[][]
{
    new int[] { 1, 2 },
    new int[] { 3, 4, 5, 6 },
    new int[] { 7 }
};

// 型推論(var)を用いた記述
var simplifiedJagged = new[]
{
    new[] { "A", "B" },
    new[] { "C", "D", "E" }
};

ジャグ配列のループ処理

ジャグ配列は各行が独立した配列オブジェクトであるため、通常の1次元配列と同じように Length プロパティを使ってアクセスできます。

C#
using System;

class Program
{
    static void Main()
    {
        int[][] jagged = new int[][]
        {
            new int[] { 10, 20 },
            new int[] { 30, 40, 50 },
            new int[] { 60 }
        };

        Console.WriteLine("ジャグ配列の内容:");
        for (int i = 0; i < jagged.Length; i++)
        {
            for (int j = 0; j < jagged[i].Length; j++)
            {
                Console.Write(jagged[i][j] + " ");
            }
            Console.WriteLine();
        }
    }
}
実行結果
ジャグ配列の内容:
10 20 
30 40 50 
60

多次元配列 vs ジャグ配列:どちらを使うべきか?

どちらの形式を使うべきかは、扱うデータの性質とパフォーマンスへの要求によって決まります。

比較表

特徴多次元配列 (int[,])ジャグ配列 (int[][])
形状常に矩形(長方形)行ごとに異なる長さを保持可能
メモリ構造連続した単一のメモリブロック配列の配列(非連続なメモリ)
アクセス速度わずかに遅い(計算コスト)高速(JITによる最適化が効きやすい)
初期化の容易さ一括で定義しやすい2段階の初期化が必要な場合がある
用途行列演算、画像データ、グリッド各行でデータ数が異なるリスト形式

パフォーマンスの裏話

意外かもしれませんが、C#においてはジャグ配列の方がアクセス速度が速い傾向にあります。

これは、.NETランタイム(JITコンパイラ)が1次元配列(ジャグ配列の各要素)の境界線チェックを最適化しやすいためです。

多次元配列の場合、インデックスの計算が複雑になるため、厳密な速度が求められるアルゴリズムではジャグ配列が選ばれることが多いです。

一方、多次元配列は「すべての行が同じ長さであること」を型レベルで保証できるため、コードの可読性や安全性が高まるというメリットがあります。

応用的な初期化手法

現代的なC#の書き方や、少し特殊な初期化方法についても触れておきます。

var(暗黙型指定)による初期化

型名が明らかな場合、var を使用してコードを簡潔に保つことができます。

C#
// 多次元配列の場合
var matrix = new int[,] { { 1, 2 }, { 3, 4 } };

// ジャグ配列の場合
var jagged = new int[][] 
{
    new int[] { 1 },
    new int[] { 2, 3 }
};

3次元以上の配列

2次元配列の延長線上で、3次元以上の配列も初期化できます。

C#
// 3次元配列(2x3x4)
int[,,] cube = new int[2, 3, 4];

// 値を指定した初期化
int[,,] cube3D = new int[,,]
{
    {
        { 1, 2 }, { 3, 4 }
    },
    {
        { 5, 6 }, { 7, 8 }
    }
};

Span<T> を活用したモダンなアプローチ

最新のC#(C# 7.2以降)では、Span<T>ReadOnlySpan<T> を使用して、配列の特定の部分を効率的に切り出す手法も一般的になっています。

2次元配列そのものを初期化するわけではありませんが、大きな配列から「行」や「特定の範囲」をゼロコピーで参照したい場合に非常に強力です。

2次元配列操作でよくある間違いと対策

初期化や操作において、エンジニアが陥りやすいミスとその回避策を紹介します。

1. 多次元配列の要素数取得に Length を使ってしまう

前述の通り、int[,] に対して Length を使うと、全次元の要素の総和が返ります。

  • 誤: for (int i = 0; i < array.Length; i++)
  • 正: for (int i = 0; i < array.GetLength(0); i++)

2. ジャグ配列の初期化忘れ

ジャグ配列は、外側を初期化しただけでは内側の配列は null のままです。

C#
int[][] jagged = new int[5][];
// jagged[0][0] = 1; // ここで NullReferenceException が発生する!
jagged[0] = new int[10]; // 必ず内側の配列を new する必要がある

3. 宣言時のカンマ忘れ

多次元配列の宣言時に [] の中にカンマを忘れると、1次元配列として扱われてしまいます。

  • int[,] : 2次元
  • int[,,] : 3次元

まとめ

C#の2次元配列には、「多次元配列」と「ジャグ配列」の2種類があり、それぞれ特性が異なることを理解するのが最も重要です。

  • 多次元配列は、データの形状が常に一定で、行列などの数学的な概念をコードに落とし込む際に適しています。
  • ジャグ配列は、行ごとにデータ長が異なる柔軟な構造に対応でき、かつ実行時のパフォーマンスに優れています。

開発するアプリケーションの要件に合わせて、適切な初期化パターンを選択してください。

特に、大量のデータを扱う際や、要素数が動的に変わる可能性がある場合は、ジャグ配列や List<List<T>> などのコレクションの使用も検討すると良いでしょう。

配列の初期化はプログラミングの基礎中の基礎ですが、正しく使い分けることで、より堅牢で効率的なC#コードを記述できるようになります。

今回の内容を参考に、最適な配列操作を実践してみてください。