多次元配列

Revised: Jan./1st/2004

Java では任意次元の配列が実現できます。具体的には、要素が配列である配列が多次元配列です。

多次元配列変数

配列を参照する変数を定義するとき、個々の要素を指定するために指定するインデックスの個数(次元)を、大括弧で明示します。

// 一個のインデックスで要素を指定する場合
int[] vals1;
// 二個のインデックスで要素を指定する場合
int[][] vals2;
// 三個のインデックスで要素を指定する場合
int[][][] vals3;

大括弧の個数を増やせば、任意の次元の配列を指定することができます。

上の例では、vals3 は、「int 型の要素を持つ三次元配列」を参照可能な変数として定義されています。このとき、要素数を指定する仕組みはなく、任意の要素数の配列を代入可能です。実際に参照先の配列を生成するには、次の何れかの仕組みを使います。

定義方法1

一次元配列の場合、次のようにリストを代入することで配列変数を初期化できます。

int[] vals = {0, 1, 2, 3};

多次元の場合は、各要素がリストであるリストで初期化することができます。

String[][] searches =
	{
		{ "Google", "goo", "Lycos", "Alltheweb"},
		{ "Yahoo", "Open Directory", "Excite"},
		{ "検索デスク", "Ultra Internet Guide"}
	};

配列 searches の各要素が配列になっています。

searches[0] = { "Google", "goo", "Lycos", "Alltheweb"};
searches[1] = { "Yahoo", "Open Directory", "Excite"};
searches[2] = { "検索デスク", "Ultra Internet Guide"};

多くの言語では、多次元配列の場合は4行6列などのブロック状に配列を作成します。 Java の場合は、各行ごとに含まれる要素数が異なることが許されます。

要素 searches[1] に入っている二番目の要素は次のように参照できます:

searches[1][1] = Open Directory;

このように、配列を要素とする配列を作ることで、任意の次元の配列を作ることが出来ます。

サンプル

この二次元配列の要素を全て出力してみます。

TwoDimensionDemo.java:

class TwoDimensionDemo {
	public static void main(String[] args) {
		// 2次元配列の定義
		String[][] searches =
			{
				{ "Google", "goo", "Lycos", "Alltheweb"},
				{ "Yahoo", "Open Directory", "Excite"},
				{ "検索デスク", "Ultra Internet Guide"}
			};
		// i = 0 から始めて i < 3 である間は繰り返し
		for (int i = 0; i < 3; i++) {
			System.out.println(i + " 番目要素");
			// j = 0 から初めて j < searches[i].length である間は繰り返し
			for (int j = 0; j < searches[i].length; j++) {
				System.out.print(searches[i][j] + "; ");
			}
			System.out.println("");
		}
	}
}

配列の出力部分は、 for ループのブロックが二つ、入れ子状になっています。これを、 for ループがネストしているとと呼びます。外側の for ループブロックは、 i が 3 未満の間、すなわち 0, 1, 2 の間は繰り返されます。内側の for ループブロックは、 j が 0, 1,... と始まり、配列の i 番目要素 searches[i] の要素の個数未満の間は繰り返します。

searches[i].length は配列 searches の i 番目要素である配列 searches[i] が参照している配列の要素の個数です。

C:\Java>javac TwoDimensionDemo.java
C:\Java>java TwoDimensionDemo
0 番目要素
Google; goo; Lycos; Alltheweb;
1 番目要素
Yahoo; Open Directory; Excite;
2 番目要素
検索デスク; Ultra Internet Guide;
C:\Java>

定義方法2

一次元配列の場合、最初に要素数を与えて空の配列を生成することができます。

int[] vals = new int[5];

多次元配列でも、主配列と副配列の要素数を指定することで初期化することが出来ます。

TestMultiDim.java:

class TestMultiDim {
	public static void main(String[] args) {
	//実行時に 0-4 までの数字を引数に与えてください。
		String[][] sck;
		sck = new String[2][5];
		sck[0][0] = "保存";
		sck[0][1] = "コピー";
		sck[0][2] = "切り抜き";
		sck[0][3] = "貼り付け";
		sck[0][4] = "全て選択";
		sck[1][0] = "Ctrl+S";
		sck[1][1] = "Ctrl+C";
		sck[1][2] = "Ctrl+X";
		sck[1][3] = "Ctrl+V";
		sck[1][4] = "Ctrl+A";
		int i = Integer.parseInt(args[0]);
		System.out.print(sck[0][i] + ": " + sck[1][i]);
	}
}

コマンドライン引数に 0-4 の数字を取って、対応する配列を出力するようにしてある。

メソッド Integer.parseInt() は、クラス Integer のメソッドで、引数に与えられた文字列を整数として解釈する働きをもつ。これがないと、 int 型の変数 i に、文字列であるコマンドライン引数 args[0] は代入できない。このような型変換は自動化されていない。

コマンドライン:

C:\Java>javac TestMultiDim.java
C:\Java>java TestMultiDim 0
保存: Ctrl+S
C:\Java>java TestMultiDim 1
コピー: Ctrl+C
C:\Java>java TestMultiDim 2
切り抜き: Ctrl+X
C:\Java>java TestMultiDim 4
全て選択: Ctrl+A
C:\Java>java TestMultiDim 3
貼り付け: Ctrl+V
C:\Java>

因みに、引数を与えなかったり、不適切な値を入れると実行時エラー(例外)が起こり、次のようなエラーメッセージが返ってくる。

C:\Java>java TestMultiDim
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
        at TestMultiDim.main(TestMultiDim.java:19)
C:\Java>java TestMultiDim 5
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
        at TestMultiDim.main(TestMultiDim.java:20)
C:\Java>

引数が与えられない場合は args[0] を呼んだ時点で例外 (Exception) が発生し、不適切な値が与えられた場合は、対応する配列の要素 sck[0][i] が呼ばれた時点で例外が発生する。

例外(実行時エラー、ランタイム・エラー、 Exception)に関しては、別項を立てて解説する。

配列の代入互換性

配列変数を定義するとき、次の二つを指定します。

例えば、次の場合は、要素の型が int 型で、要素にアクセスするためのインデックスの個数は 3 つです。具体的な値で初期化されていないので、各次元ごとにとり得る要素の個数は未決定です。

int[][][] vals;

vals には、三次元 int 型配列が代入可能です。vals[0]vals[1] など、一個だけインデックスを指定すると、残り二個のインデックスで指定される要素の集合である配列が代入可能です。vals[0][0]vals[0][1] など、二つインデックスを指定すると、一次元配列が代入可能です。三つとも指定すると、個別の int 型要素を指定したことになります。

次のサンプルは、3×4×5の配列 val1 の、val1[0]val1[0][0] に、代入互換の配列の参照を代入しています。要素数が、代入されたものの要素数に変化していることが確認できます。

ArrayDemo.java:

class ArrayDemo {
	public static void main(String[] args) {
		int[][][] val1 = new int[3][4][5];
		System.out.println("val1 = new int[3][4][5];");
		System.out.println("\t val1: " + val1.length);
		System.out.println("\t val1[0]: " + val1[0].length);
		System.out.println("\t val1[0][0]: " + val1[0][0].length);
		
		int[][] val2 = new int[6][7];
		val1[0] = val2;
		System.out.println("val1[0] = new int[6][7];");
		System.out.println("\t val1: " + val1.length);
		System.out.println("\t val1[0]: " + val1[0].length);
		System.out.println("\t val1[0][0]: " + val1[0][0].length);
		int[] val3 = new int[8];
		val1[0][0] = val3;
		System.out.println("val1[0][0] = new int[8];");
		System.out.println("\t val1: " + val1.length);
		System.out.println("\t val1[0]: " + val1[0].length);
		System.out.println("\t val1[0][0]: " + val1[0][0].length);
	}
}
C:\java>javac ArrayDemo.java
C:\java>java ArrayDemo
val1 = new int[3][4][5];
         val1: 3
         val1[0]: 4
         val1[0][0]: 5
val1[0] = new int[6][7];
         val1: 3
         val1[0]: 6
         val1[0][0]: 7
val1[0][0] = new int[8];
         val1: 3
         val1[0]: 6
         val1[0][0]: 8
C:\java>

上記の実行結果を、リストで表現してみましょう。

まず、最初に配列変数 val1 が初期化された状態では、次のようになります。

// int[][][] val1 = new int[3][4][5];
val1 =
{
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
}

val1[0] は4×5行列で、6×7行列を代入すると次のようになります。

// val1[0] = new int[6][7];
val1 =
{
	{  {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0}  },
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
}

いま、 val1[0][0] は要素数が7個のベクトルとなっており、次に要素数が8個のベクトルを代入すると、次のようになります。

// val1[0][0] = new int[8];
val1 =
{
	{  {0, 0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0, 0, 0}  },
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
	{  {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0},
	   {0, 0, 0, 0, 0}  },
}

配列変数は、要素の型が代入互換であり、かつ、次元が等しいときに代入互換です。特に、多次元配列の場合は、途中までインデックスを指定した配列要素に関しても、同様の規則が成り立ちます。



Copyright © 2001-2004 SUGAI, Manabu. All Rights Reserved.