07_配列

対象:C言語経験者 / 宣言・生成の構文差分と、Javaならではの安全機能を理解します


■ 1. C言語との差分まとめ

項目C言語Java
宣言の書き方int a[5];int[] a = new int[5];
[] の位置変数名の後ろ型の後ろ(推奨)
要素数の取得自分で管理(マクロなど)a.length で取得できます
初期値不定(ゴミ値)自動で 0 / false / null に初期化されます
範囲外アクセスメモリ破壊(未定義動作)ArrayIndexOutOfBoundsException(実行時エラー)
配列は参照型ポインタで扱う参照型として扱います(null 代入可)
多次元配列int a[3][4];int[][] a = new int[3][4];

■ 2. 宣言と生成

● new による生成

Javaの配列は new キーワードで生成します。new を使うと JVM がメモリを確保してくれます。C言語の malloc に相当しますが、free は不要(GCが自動解放)です。

C言語
int a[5];         // スタック上に確保
int *b = malloc(5 * sizeof(int));
// 使い終わったら free(b) が必要
Java
int[] a = new int[5];  // ヒープ上に確保
// free 不要(GCが自動解放)

● 宣言の4パターン

// パターン1:宣言と生成を別々に
int[] a;
a = new int[5];

// パターン2:宣言と同時に生成
int[] a = new int[5];

// パターン3:初期値を指定(要素数は自動で決まる)
int[] a = {10, 20, 30, 40, 50};

// パターン4:new と初期値を同時に(パターン3と同じ意味)
int[] a = new int[]{10, 20, 30, 40, 50};
[] はJavaでは型の後ろに書くのが推奨です。
int[] aint a[] は両方書けますが、Javaの慣習は int[] です。

■ 3. 配列のメモリイメージ

配列は参照型です。変数は配列本体を直接持つのではなく、配列が置かれている場所を参照します。

int[] a = new int[5]; a[0] = 10; a[1] = 20; 変数 a(スタック) 0x2f4 配列本体(ヒープ) 10 a[0] 20 a[1] 0 a[2] 0 a[3] 0 a[4] 代入していない要素は自動で 0 a.length = 5
変数 a は配列の場所(アドレス)を持ちます。配列本体はヒープに置かれ、未代入の要素は 0 に初期化されています。

● .length で要素数を取得する

int[] a = new int[5];
System.out.println(a.length);   // → 5

int[] b = {10, 20, 30};
System.out.println(b.length);   // → 3
C言語では配列の要素数を自分で管理する必要がありましたが、Javaでは .length でいつでも取得できます。
ループの終了条件を i < a.length と書けば、配列のサイズが変わっても修正不要になります。

■ 4. 範囲外アクセス(例外)

C言語では範囲外を読み書きしてもエラーにならずにメモリを壊しました。Javaは範囲外アクセスを実行時に検出して例外を投げます。

int[] a = new int[3];   // インデックス 0, 1, 2 が有効

a[0] = 10;   // OK
a[2] = 30;   // OK
a[3] = 40;   // 実行時エラー:ArrayIndexOutOfBoundsException
             // 「配列インデックスが範囲外」という意味

エラーを実際に確認しましょう

07_Array フォルダに ArrayError.java を作って実行しましょう。

public class ArrayError {
    public static void main(String[] args) {
        int[] a = new int[3];
        a[0] = 10;
        a[3] = 40;   // ← 範囲外
        System.out.println(a[0]);
    }
}

ターミナルに次のようなエラーが表示されます:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3 at ArrayError.main(ArrayError.java:5)

「Index 3 out of bounds for length 3」→「長さ3の配列にインデックス3でアクセスしようとした」という意味です。
エラーメッセージに ファイル名と行番号 が表示されるので、どこが原因かすぐわかります。

確認したら a[3]a[2] に直して正常動作も確認しましょう。


■ 5. 多次元配列

● 宣言と生成

C言語
int a[3][4];

// 初期値あり
int b[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};
Java
int[][] a = new int[3][4];

// 初期値あり
int[][] b = {
    {1, 2, 3},
    {4, 5, 6}
};

● 要素へのアクセスと length

int[][] b = {
    {1, 2, 3},
    {4, 5, 6}
};

System.out.println(b.length);     // → 2(行数)
System.out.println(b[0].length);  // → 3(0行目の列数)
System.out.println(b[1][2]);      // → 6(1行目 2列目)
b[0] b[1] [0] [1] [2] 1 2 3 4 5 6 b[1][2] = 6 b[0].length = 3
b.length は行数(2)、b[0].length は0行目の列数(3)を返します。

■ 実習1:1次元配列の操作

実習1

07_Array フォルダに Array1D.java を作って実行しましょう。

public class Array1D {
    public static void main(String[] args) {

        // ① 生成と初期値の確認
        int[] a = new int[5];
        System.out.println("初期値: a[0]=" + a[0] + ", a[4]=" + a[4]);

        // ② 値の代入
        for (int i = 0; i < a.length; i++) {
            a[i] = (i + 1) * 10;   // 10, 20, 30, 40, 50
        }

        // ③ 拡張for文で全要素を出力
        System.out.print("要素: ");
        for (int x : a) {
            System.out.print(x + " ");
        }
        System.out.println();

        // ④ 最大値を探す
        int max = a[0];
        for (int x : a) {
            if (x > max) {
                max = x;
            }
        }
        System.out.println("最大値: " + max);

        // ⑤ 初期値リテラルで作成
        int[] b = {3, 1, 4, 1, 5, 9, 2, 6};
        System.out.println("b.length = " + b.length);
    }
}

実行結果:

初期値: a[0]=0, a[4]=0 要素: 10 20 30 40 50 最大値: 50 b.length = 8

● 動いたら変えてみましょう

#変えてみること確認ポイント
1 ④ の最大値を探すコードを最小値を探すコードに書き換える maxmin>< に変えるだけで動きます
2 int[] a = new int[5]boolean[] flags = new boolean[3] に変えて初期値を確認する boolean の初期値は falseString の初期値は null になります
3 ⑤ の配列 b を拡張for文で合計と平均を求めるコードを追加してみる 実習2(LoopAdvanced)でやった内容の応用です。自分で書けるか試しましょう

■ 実習2:2次元配列の操作

実習2

08_Array2D フォルダに Array2D.java を作って実行しましょう。

public class Array2D {
    public static void main(String[] args) {

        // ① 2次元配列の生成と代入
        int[][] table = new int[3][4];
        for (int row = 0; row < table.length; row++) {
            for (int col = 0; col < table[row].length; col++) {
                table[row][col] = row * 10 + col;
            }
        }

        // ② 表形式で出力
        System.out.println("--- table ---");
        for (int row = 0; row < table.length; row++) {
            for (int col = 0; col < table[row].length; col++) {
                System.out.printf("%4d", table[row][col]);
            }
            System.out.println();
        }

        // ③ 初期値リテラルで2次元配列
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        // ④ 行数と列数
        System.out.println("\nmatrix: " + matrix.length + "行 "
                            + matrix[0].length + "列");

        // ⑤ 対角成分を出力(行と列のインデックスが同じ要素)
        System.out.print("対角: ");
        for (int i = 0; i < matrix.length; i++) {
            System.out.print(matrix[i][i] + " ");
        }
        System.out.println();
    }
}

実行結果:

--- table --- 0 1 2 3 10 11 12 13 20 21 22 23 matrix: 3行 3列 対角: 1 5 9

● 動いたら変えてみましょう

#変えてみること確認ポイント
1 ② の出力ループを拡張for文に書き換えてみる for (int[] row : table)for (int val : row) の2重拡張for文で書けます。インデックスが不要なことを確認しましょう
2 ③ の matrix の全要素の合計を求めるコードを追加する 2重ループで全要素を足し合わせましょう。答えは 1+2+…+9 = 45 になります
3 matrix[3][0] にアクセスしてみる(行数は3なので存在しない) ArrayIndexOutOfBoundsException が発生することを確認しましょう

■ 試験(問4・問5)との対応

頻出ポイント

よく問われること正しい答え
配列の初期値int0booleanfalse、参照型は null
要素数の取得配列名.length(メソッドではなくフィールド。() 不要)
範囲外アクセスコンパイルエラーではなく ArrayIndexOutOfBoundsException(実行時エラー)
2次元配列の行数a.length
2次元配列の列数a[0].length
.lengthフィールド(変数)なので () は不要です。a.length() と書くとコンパイルエラーになります。
String.length() はメソッドなので () が必要。混同しやすいので注意しましょう。
練習問題:次の出力を答えましょう(第67回 問4(22) 類似)
public class Q {
    public static void main(String[] args) {
        int[] data = {3, 5, 6, 9, 10, 13, 16};
        for (int i = 0; i < data.length; i += 2) {
            System.out.print(data[i] + ", ");
        }
    }
}
3, 6, 10, 16,

i が 0, 2, 4, 6 と 2 ずつ増えます。data[0]=3, data[2]=6, data[4]=10, data[6]=16 が出力されます。

練習問題2:次のコードの結果を答えましょう
public class Q2 {
    public static void main(String[] args) {
        String[] names = new String[3];
        System.out.println(names[0]);
        System.out.println(names.length);
    }
}
null 3

参照型(String)の配列は、生成直後の要素が null に初期化されます。length は要素数の 3 を返します。


■ まとめ

ポイント内容
宣言int[] a = new int[5];[] は型の後ろに書くのが慣習
初期値自動初期化。int は 0、boolean は false、参照型は null
.length要素数を取得。() 不要(フィールド)
範囲外アクセスArrayIndexOutOfBoundsException。C言語のような無音のメモリ破壊は起きません
2次元配列int[][] a = new int[行][列];。行数は a.length、列数は a[0].length
new配列はヒープに確保。free 不要(GC が解放)