カテゴリー
Java

Java(Step6-2)

配列

多次元配列

前の記事では、構成要素が直線状に並んだ配列を説明しました。構成要素自体が配列となっている複雑な構造の配列を、多次元配列と呼びます。

多次元配列

配列が構成要素型になっている配列が2次元配列です。そして、2次元配列が構成要素型となっている配列が3次元配列になります。これらを1次元配列と区別して多次元配列(multidimensional array)と言います。

2次元配列

まずは、多次元配列の中で最も単純な構造のint型の2次元配列を考えてみましょう。その実態は次のようになります。

『int 型を構成要素型とする配列』を構成要素とする配列

この配列の型は int[][] であり、以下に示すどれを使用しても宣言できます。

  1. int[][] x;
  2. int[] x[]; 『int 型を構成要素型とする配列』を構成要素とする配列の宣言
  3. int x[][];

具体的な例を考えていきましょう。『int 型を構成要素型とする要素数3の配列』を構成要素とする要素数2の配列

本体の生成を同時に行うと、配列変数の宣言は以下のようになります。

int[][] x = new int[3][2];

この宣言によって生成される配列xのイメージを表したのが次の図になります。

2次元配列のイメージ
3次元配列

続いて、3次元配列です。例えば、long型の3次元配列の型はlong[][][]になります。ここでは、以下の配列を考えてみましょう。

long[][][] y = new long[2][3][4];

ここで宣言されたyの型は、次のようになります。

『long型を構成要素型とする配列を構成要素型とする配列』を構成要素型とする配列

2次元配列xと、3次元配列yの構成要素型は、それぞれ次のようになっています。

  • x…int 型を構成要素型とする配列
  • y…long 型を構成要素型とする配列を構成要素型とする配列

これらの配列を、それ以上分解できない要素にまで分解すると、配列xはint型となり、配列yはlong型となります。このような型を要素型(element type)と呼び、要素型レベルの構成要素を要素(element)と呼びます。そして、全要素の個数が要素数です。

プログラム例

2次元配列を生成して全要素を0から99の乱数で格納するプログラムを以下に示します。

//Array2D.java
//二次元配列を生成して全要素を乱数で埋め尽くす
import java.util.Random;
import java.util.Scanner;

public class Array2D {
    public static void main(String[] args){
        Random rand = new Random();
        Scanner stdIn = new Scanner(System.in);

        System.out.print("行数:"); //行数を読み込む
        int h = stdIn.nextInt();

        System.out.print("列数:"); //列数を読み込む
        int w = stdIn.nextInt();

        int[][] x = new int[h][w];

        for(int i = 0; i < h; i++)
            for(int j = 0; j < w; j++){
                x[i][j] = rand.nextInt(100);
                System.out.println("x[" + i + "][" + j + "] = " + x[i][j]);
            }
        stdIn.close();
    }
}
Array2D.java実行結果

もう一つのプログラムでは、2つの行数の和を求めて表示するプログラムになっています。

//Matrix.java
//2行3列の行数を加算する

public class Matrix {
    public static void main(String[] args){
        int[][] a = {{1,2,3}, {4,5,6}};
        int[][] b = {{6,3,4}, {5,1,2}};
        int[][] c = {{0,0,0},{0,0,0}};

        for(int i = 0; i < 2; i++)
            for(int j = 0; j < 3; j++)
                c[i][j] = a[i][j] + b[i][j];
        
        System.out.println("行列a:");
        for(int i = 0; i < 2; i++){
            for(int j = 0; j < 3; j++)
                System.out.printf("%3d", a[i][j]);
            System.out.println();
        }

        System.out.println("行列b:");
        for(int i = 0; i < 2; i++){
            for(int j = 0; j < 3; j++)
                System.out.printf("%3d", b[i][j]);
            System.out.println();
        }

        System.out.println("行列c:");
        for(int i = 0; i < 2; i++){
            for(int j = 0; j < 3; j++)
                System.out.printf("%3d", c[i][j]);
            System.out.println();
        }
    }    
}
Matrix.java実行結果

多次元配列の内部

多次元配列の内部を詳しく説明していきます。まずは、配列変数の宣言と本体の生成を個別に行います。以下のように宣言、処理を分解します。

int[][] x;
x = new int[2][];
x[0] = new int[2];
x[1] = new int[2];

実に4段階も分解でき、2次元配列の内部構造が複雑なことを示します。以下の図を見ながら理解を深めましょう。

2次元配列の物理的なイメージ
  1. 2次元配列xの宣言になります。int[][]型のxは、配列本体ではなく、配列変数です。

2. 配列本体を生成するとともに、xがそれを参照するように代入を行います。ここで生成するのは、以下の配列になります。

構成要素型がint[]型で構成要素数が2の配列

3. 配列本体を生成するとともに、x[0]がそれを参照するように代入を行います。ここで生成するのは、以下の配列になります。

構成要素型がint[]型で構成要素数が2の配列

4. 配列本体を生成するとともに、x[1]がそれを参照するように代入を行います。ここで生成するのは、以下の配列になります。

構成要素型がint[]型で構成要素数が2の配列

凸凹な2次元配列の内部

上記の配列xの構成要素であるx[0]とx[1]は、それぞれが独立した配列変数です。そのため、配列の要素数は同一である必要はありません。生成する個々の配列の要素数を異なるものにすれば、凸凹な配列になります。

//UnevennessArray.java
//凹凸な2次元配列

public class UnevennessArray {
    public static void main(String[] args){
        int[][] c;
        c = new int[3][];
        c[0] = new int[5];
        c[1] = new int[3];
        c[2] = new int[4];

        for(int i = 0; i < c.length; i++){
            for(int j = 0; j < c[i].length; j++)
                System.out.printf("%3d", c[i][j]);
            System.out.println();
        }
    }
}
UnevennessArray.java実行結果

変数cが参照するのは、以下の配列です。

  • c…構成要素型がint[]型で構成要素数が3の配列

そして、各構成要素 c[0]、c[1]、c[2]は、以下の配列を参照します。

  • c[0]…構成要素型がint[]型で構成要素数が5の配列
  • c[1]…構成要素型がint[]型で構成要素数が3の配列
  • c[2]…構成要素型がint[]型で構成要素数が4の配列

初期化子

以下の例を見てましょう。配列aに対して初期化子が与えられています。この初期化子は、縦横に並べて宣言すると読みやすくなります。

int[][]a = {
   {1, 2, 3},   //0行目の要素に対する初期化子
   {4, 5, 6},   //1行目の要素に対する初期化子
};

0行目と1行目の間のコンマはなくても構いません、しかし、以下のメリットがあります。

  • 初期化子を縦に並べた際の見かけ上のバランスがとれる。
  • 行単位での初期化子の追加・削除が容易になる。