配列&コレクション

【C#】配列初期化の基本手法と実例:サイズ指定から多次元配列までの具体的な方法

C#の配列初期化は、サイズ指定と初期値指定の2通りがあり、用途に合わせて選べます。

例えば、int[] numbers = new int[5]と宣言すると各要素は自動で0になりますし、int[] numbers = {1, 2, 3}と書くことで各要素に指定の値が入ります。

また、2次元以上やジャグ配列も柔軟に定義でき、扱いやすい点が魅力です。

配列初期化の基本構文

サイズ指定による配列初期化

宣言とメモリ割当

まず、サイズを指定して配列を初期化する場合は、newキーワードを用いてメモリ領域を確保します。

例えば、整数型の配列を以下のように宣言すると、指定したサイズ分の要素がメモリ上に確保されます。

サンプルコードでは、配列の宣言とメモリ割当の流れを確認してください。

using System;
class Program
{
    static void Main()
    {
        // 整数型配列のサイズを5と指定してメモリを割り当てます。
        int[] numbers = new int[5];
        // 配列の各要素を表示します。宣言時は自動的に初期化されるため、整数型の場合は全要素が0になります。
        Console.WriteLine("サイズ指定による初期化結果:");
        for (int i = 0; i < numbers.Length; i++)
        {
            Console.WriteLine("インデックス " + i + " の値:" + numbers[i]);
        }
    }
}
サイズ指定による初期化結果:
インデックス 0 の値:0
インデックス 1 の値:0
インデックス 2 の値:0
インデックス 3 の値:0
インデックス 4 の値:0

この方法では、配列のサイズを指定した分だけメモリ上に領域が確保され、整数型であれば全要素が0、文字列型であれば全要素がnullに初期化されます。

自動初期値の設定

C#の配列初期化では、型ごとに定められた初期値が自動的に設定されます。

たとえば、数値型は0、ブール型はfalse、参照型はnullといった具合です。

以下のコード例は、整数型と文字列型の配列に自動初期値が設定される様子を示しています。

using System;
class Program
{
    static void Main()
    {
        // 整数型配列をサイズ指定で初期化すると、全要素が0になります。
        int[] intArray = new int[3];
        // 文字列型配列をサイズ指定で初期化すると、全要素がnullとなります。
        string[] strArray = new string[3];
        Console.WriteLine("整数配列の自動初期値:");
        foreach (int value in intArray)
        {
            Console.WriteLine(value);
        }
        Console.WriteLine("文字列配列の自動初期値:");
        foreach (string str in strArray)
        {
            // null値の場合は「null」と表示するための条件を記述
            Console.WriteLine((str == null) ? "null" : str);
        }
    }
}
整数配列の自動初期値:
0
0
0
文字列配列の自動初期値:
null
null
null

型に合わせた初期値のおかげで、配列宣言後すぐに値を利用することができ、初期化漏れによるエラーが起こりにくいメリットがあります。

初期値指定による配列初期化

宣言と初期値の同時設定

配列の宣言と同時に初期値を設定する方法として、初期化リストを利用する方法があります。

変数宣言時に初期値を設定することで、コードの可読性が向上し、すぐに使用可能な配列を生成できます。

以下のコード例では、最初から値を設定して配列を作成する方法を示しています。

using System;
class Program
{
    static void Main()
    {
        // 宣言時に初期値を渡して配列を定義します。
        int[] numbers = new int[] { 1, 2, 3, 4, 5 };
        Console.WriteLine("初期値指定による配列内容:");
        for (int i = 0; i < numbers.Length; i++)
        {
            Console.WriteLine("要素 " + i + ":" + numbers[i]);
        }
    }
}
初期値指定による配列内容:
要素 0:1
要素 1:2
要素 2:3
要素 3:4
要素 4:5

この方法は、宣言と同時に初期値を与えるため、後で値を個々に代入する手間が省け、コードもシンプルになります。

初期値リストの利用パターン

簡潔な記述のために型推論機能を活用し、初期値リストをそのまま割り当てることもできます。

次のコード例では、配列宣言の際に初期値リストのみを記述しているため、C#のコンパイラが自動的に型を推論してくれます。

using System;
class Program
{
    static void Main()
    {
        // 初期値リストを用いて配列を初期化します。
        var fruits = new[] { "Apple", "Banana", "Cherry" };
        Console.WriteLine("初期値リストを用いた配列内容:");
        for (int i = 0; i < fruits.Length; i++)
        {
            Console.WriteLine("果物 " + i + ":" + fruits[i]);
        }
    }
}
初期値リストを用いた配列内容:
果物 0:Apple
果物 1:Banana
果物 2:Cherry

このように、短く記載できるので、読みやすく柔軟なコードを書く際に便利です。

多次元配列の初期化方法

2次元配列の初期化

明示的な初期値設定

2次元配列は、行と列が決まっている配列として利用されることが多く、あらかじめ初期値を設定しておく方法がよく使われます。

次の例では、2行3列の配列に値を直接定義しています。

メモリ上の配列がどのように構成されるかを確認してみましょう。

using System;
class Program
{
    static void Main()
    {
        // 2次元配列matrixを宣言し、初期値を設定します。
        int[,] matrix = new int[2, 3]
        {
            { 1, 2, 3 },
            { 4, 5, 6 }
        };
        Console.WriteLine("2次元配列の初期値設定結果:");
        for (int i = 0; i < 2; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                Console.Write(matrix[i, j] + " ");
            }
            Console.WriteLine();
        }
    }
}
2次元配列の初期値設定結果:
1 2 3
4 5 6

インデックスアクセスの基本

初期化後は、インデックスを利用して各要素にアクセスすることができます。

行と列のインデックスを使うため、指定した範囲内でアクセスする必要があり、外れた場合はエラーが発生します。

下記のサンプルコードでは、特定の要素にアクセスし、その値を変更する方法を示しています。

using System;
class Program
{
    static void Main()
    {
        int[,] matrix = new int[2, 3]
        {
            { 1, 2, 3 },
            { 4, 5, 6 }
        };
        // インデックスを用いて要素にアクセスします。
        Console.WriteLine("変更前のmatrix[1,2]:" + matrix[1, 2]);
        // matrixの特定の要素を書き換えます。
        matrix[1, 2] = 10;
        Console.WriteLine("変更後のmatrix[1,2]:" + matrix[1, 2]);
    }
}
変更前のmatrix[1,2]:6
変更後のmatrix[1,2]:10

このような操作により、2次元配列の内容を柔軟に変更できます。

高次元配列の初期化

3次元以上の配列定義

高次元配列は、より複雑なデータ管理が必要な場合に利用されます。

例えば、3次元配列は立体的なデータの格納に利用でき、行、列、奥行きを持つ配列として宣言することができます。

以下のコード例は、3次元配列の宣言方法と初期値の設定方法を示しています。

using System;
class Program
{
    static void Main()
    {
        // 3次元配列の宣言と初期値の設定を行います。
        int[,,] cube = new int[2, 2, 2]
        {
            {
                { 1, 2 },
                { 3, 4 }
            },
            {
                { 5, 6 },
                { 7, 8 }
            }
        };
        Console.WriteLine("3次元配列cubeの内容:");
        for (int i = 0; i < 2; i++)
        {
            for (int j = 0; j < 2; j++)
            {
                for (int k = 0; k < 2; k++)
                {
                    Console.Write("cube[" + i + "," + j + "," + k + "]=" + cube[i, j, k] + "  ");
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }
}
3次元配列cubeの内容:
cube[0,0,0]=1  cube[0,0,1]=2  
cube[0,1,0]=3  cube[0,1,1]=4  

cube[1,0,0]=5  cube[1,0,1]=6  
cube[1,1,0]=7  cube[1,1,1]=8  

複数次元の要素アクセス

高次元配列の各要素には、それぞれのインデックスを用いてアクセスできます。

各インデックスの意味を理解することが重要です。

以下は、3次元配列の特定の位置にある値へアクセスし、変更を加える例です。

using System;
class Program
{
    static void Main()
    {
        int[,,] cube = new int[2, 2, 2]
        {
            {
                { 1, 2 },
                { 3, 4 }
            },
            {
                { 5, 6 },
                { 7, 8 }
            }
        };
        // 3次元配列の要素へアクセスし、値を表示します。
        Console.WriteLine("変更前のcube[1,0,1]:" + cube[1, 0, 1]);
        // 3次元配列の特定要素に新たな値を代入します。
        cube[1, 0, 1] = 20;
        Console.WriteLine("変更後のcube[1,0,1]:" + cube[1, 0, 1]);
    }
}
変更前のcube[1,0,1]:6
変更後のcube[1,0,1]:20

このように、複数次元の配列でもインデックス操作を正しく行うことで、任意の要素を柔軟に取り扱うことが可能です。

ジャグ配列の取り扱い

ジャグ配列の宣言と初期化

各要素の配列サイズ設定

ジャグ配列は、配列の各要素が別々の長さを持つ配列になっている形式です。

サイズが固定されていない場合や、各要素に異なるデータ数を格納したい場合に有効です。

以下のサンプルコードでは、ジャグ配列の宣言と各要素のサイズを設定する方法を示します。

using System;
class Program
{
    static void Main()
    {
        // ジャグ配列を宣言し、サイズを指定します。
        int[][] jaggedArray = new int[3][];
        jaggedArray[0] = new int[2];  // 1つ目の要素は2つの整数を持ちます。
        jaggedArray[1] = new int[3];  // 2つ目は3つの整数を持ちます。
        jaggedArray[2] = new int[1];  // 3つ目は1つの整数だけ持ちます。
        // 各要素に初期値を代入する例
        jaggedArray[0][0] = 10;
        jaggedArray[0][1] = 20;
        jaggedArray[1][0] = 30;
        jaggedArray[1][1] = 40;
        jaggedArray[1][2] = 50;
        jaggedArray[2][0] = 60;
        Console.WriteLine("ジャグ配列の内容:");
        for (int i = 0; i < jaggedArray.Length; i++)
        {
            Console.Write("配列 " + i + ":");
            for (int j = 0; j < jaggedArray[i].Length; j++)
            {
                Console.Write(jaggedArray[i][j] + " ");
            }
            Console.WriteLine();
        }
    }
}
ジャグ配列の内容:
配列 0:10 20
配列 1:30 40 50
配列 2:60

上記の例では、ジャグ配列のそれぞれの内部配列に対して、異なるサイズの配列を設定しており、必要に応じて各要素に値を代入できる点が特徴です。

ジャグ配列の操作と注意点

可変長配列の管理

ジャグ配列は、各内部配列のサイズが異なるため、配列の境界を意識する必要があります。

反復処理を行う際は、各内部配列のサイズを取得してループ処理を実施すれば、例外が発生しにくくなります。

以下の例では、ジャグ配列の可変長性を管理するための基本的なループ処理を行っています。

using System;
class Program
{
    static void Main()
    {
        int[][] jaggedArray = new int[2][];
        jaggedArray[0] = new int[] { 5, 10, 15 };
        jaggedArray[1] = new int[] { 20, 25 };
        Console.WriteLine("可変長ジャグ配列の操作例:");
        for (int i = 0; i < jaggedArray.Length; i++)
        {
            Console.Write("配列 " + i + " の全要素:");
            for (int j = 0; j < jaggedArray[i].Length; j++)
            {
                Console.Write(jaggedArray[i][j] + " ");
            }
            Console.WriteLine();
        }
    }
}
可変長ジャグ配列の操作例:
配列 0 の全要素:5 10 15 
配列 1 の全要素:20 25 

このように、ジャグ配列の操作では、内部配列ごとにサイズが異なる点に注意しながらループ処理を実装すると良いです。

初期化方法の選択とエラー回避

パフォーマンス向上の視点

初期化タイミングの最適化

配列の初期化は、必要なタイミングで行うことがパフォーマンス向上につながります。

使用しない時に不要なメモリ割当を避ける工夫や、初期化処理を遅延させる方法が考えられます。

プログラムの実行順序に合わせたタイミングで初期化を行うと、処理全体の効率が向上します。

メモリ再利用の工夫

配列生成後に頻繁に新しい配列を作成すると、ガベージコレクションが発生しやすくなります。

可能な場合、再利用可能な配列を保持し、必要に応じて値を更新する方法を検討することで、メモリの使用効率が改善できます。

処理効率の改善

短い繰り返し処理の場合、ループの条件チェックを最小限にする工夫や、キャッシュメモリの活用を意識することによって、配列初期化後や要素操作時の処理効率を上げることができます。

特に大規模なデータセットを扱う際は、細かい最適化が効果を発揮します。

インデックスエラー防止策

境界チェックの実装

配列にアクセスする際は、各インデックスが有効な範囲内にあるかを確認するための境界チェックを行うことが大切です。

たとえば、ループ処理内で配列のLengthプロパティを用いることで、意図しないアクセスによる例外を防ぐことができます。

下記のコードは、インデックスのチェックをしながら配列の要素を出力する例です。

using System;
class Program
{
    static void Main()
    {
        int[] numbers = { 10, 20, 30, 40, 50 };
        Console.WriteLine("境界チェックを行う例:");
        for (int i = 0; i < numbers.Length; i++)
        {
            // 有効なインデックスかどうかを確認しつつ要素を出力します。
            if (i >= 0 && i < numbers.Length)
            {
                Console.WriteLine("インデックス " + i + ": " + numbers[i]);
            }
            else
            {
                Console.WriteLine("不正なインデックス:" + i);
            }
        }
    }
}
境界チェックを行う例:
インデックス 0: 10
インデックス 1: 20
インデックス 2: 30
インデックス 3: 40
インデックス 4: 50

型安全性の確保

配列の型が固定されているため、型安全性はコンパイラによって保たれる仕組みとなっています。

もし異なる型が混在する場合は、ジェネリック型やコレクションを利用することで、意図しない型エラーを防止することができます。

これにより、配列操作中のミスを未然に防ぐ効果が期待できます。

配列初期化の応用事例

データ管理における配列利用

固定データの管理例

配列は、固定データの管理に適していると言えます。

たとえば、曜日の一覧や月の名前など、変更されないデータを配列に格納しておくと、一定の処理で素早く参照することが可能です。

以下は、曜日を配列に格納し、順番に出力するサンプルコードです。

using System;
class Program
{
    static void Main()
    {
        // 曜日を固定データとして配列に格納します。
        string[] weekDays = { "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日", "日曜日" };
        Console.WriteLine("固定データとしての曜日一覧:");
        foreach (string day in weekDays)
        {
            Console.WriteLine(day);
        }
    }
}
固定データとしての曜日一覧:
月曜日
火曜日
水曜日
木曜日
金曜日
土曜日
日曜日

この例では、変更の必要がないデータを配列に格納することで、プログラム全体の処理がシンプルになるメリットがあります。

動的データ格納の事例

動的なデータを扱う場合でも、配列は基本的な管理手段として利用できます。

たとえば、ユーザーからの入力データを一時的に配列に保存し、その後まとめて処理する、といった使い方が考えられます。

動的なデータの場合は、必要に応じて配列のサイズを変更するための手法(たとえば、リストへの変換など)が有効です。

他のデータ構造との連携

リストやコレクションとの比較

配列は固定サイズであるため、サイズ変更が必要な場合はList<T>などのコレクションと連携するのが一般的です。

配列とコレクションの違いを理解することで、用途に応じたデータ管理が実現できます。

以下は、配列からList<int>へ変換し、操作するサンプルコードです。

using System;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        // 固定サイズの配列を用意します。
        int[] numbers = { 1, 2, 3, 4, 5 };
        // 配列からListに変換します。
        List<int> numberList = new List<int>(numbers);
        // Listに新たな要素を追加して柔軟に管理します。
        numberList.Add(6);
        Console.WriteLine("配列から生成したListの内容:");
        foreach (int num in numberList)
        {
            Console.WriteLine(num);
        }
    }
}
配列から生成したListの内容:
1
2
3
4
5
6

このような連携を意識することで、固定データと動的データの双方を効率的に管理し、必要な処理を柔軟に実装することができます。

まとめ

今回の記事では、C#における配列初期化のさまざまな手法について紹介しました。

サイズ指定と初期値指定の基本構文から始まり、2次元以上の多次元配列の初期化、ジャグ配列の扱い、初期化方法の選択に伴うエラー回避やパフォーマンス向上の工夫、さらに固定データと動的データの応用事例にいたるまで、幅広い内容を取り上げました。

各コード例には、実行可能なMain関数を含め、実際に動かして確認できる形にしているため、プログラムの理解やデバッグに役立つ内容となっています。

これらの知識を参考に、用途に合わせた配列操作を実装していただければ幸いです。

関連記事

Back to top button