目次 | | | 索引 Java言語規定
第2版

10. 配列

Javaプログラム言語における 配列(arrays) は,オブジェクト(4.3.1)とし,動的に生成され,型 Object(4.3.2)の変数に代入してよい。すべてのクラスObject のメソッドは,配列に対して呼び出してよい。

配列オブジェクトは,多くの変数を含む。変数の数はゼロであってもよい。この場合,その配列は空(empty)という。配列に含まれる変数は,名前をもたない。その代わりに,負でない整数インデクス値を用いる配列アクセス式で参照する。これらの変数は,配列の構成要素(component)と呼ぶ。配列が n個の構成要素をもてば,n を配列の長さとする。配列の構成要素は,0から n- 1までの整数インデクスを使用して参照する。

配列の構成要素はすべて,配列の 構成要素型(component type)と呼ぶ同じ型をもつ。配列の構成要素型がT ならば,その配列自体の型をT[] とする。

floatの配列の構成要素の値は,常に単精度数値集合(4.2.3)の要素とする。同様に,型doubleの配列の構成要素の値は常に倍精度数値集合の要素とする。型floatの配列構成要素の値が,単精度数値集合の要素ではない単精度指数部拡張数値集合の要素であることと,型doubleの配列構成要素の値が,倍精度数値集合の要素ではない倍精度指数部拡張数値集合の要素であることは許されない。

配列の構成要素型は,それ自体が配列の型となってもよい。その配列の構成要素は,下位配列への参照を含んでもよい。任意の配列型から始めて,その構成要素を考え,構成要素も配列型ならばその構成要素を考え,などとしていったときに,いつかは配列型ではない構成要素型に到達しなければならない。この型を元の配列の要素型(element type)と呼び,データ構造のこのレベルの構成要素を,元の配列の 要素(element)と呼ぶ。

配列の要素を配列とできる状況が幾つか存在する。要素の型が Object又はCloneable又はjava.io.Serializableならば,要素の幾つか又はすべてが配列であってよい。これは,任意の配列オブジェクトをそれらの型の変数に代入可能であることによる。

10.1 配列型

配列型は,要素の名前に幾つかの空の角括弧対[]を続けて書く。角括弧対の数は,配列の入れ子の深さを示す。配列の長さは,その型の一部とはしない。

配列の要素型は,基本型又は参照型の任意の型であってよい。特に次のとおりとする。

配列型は,宣言及びキャスト式(15.16)で使用する。

10.2 配列変数

配列型の変数は,オブジェクトへの参照をもつ。配列型の変数の宣言は,配列オブジェクトの生成又は配列構成要素のための空間の割当ては行わず,配列への参照を含むことが可能な変数だけを生成する。しかしながら,宣言子(8.3)の初期化子部分は,変数の初期値を参照する配列を生成してもよい。

配列の長さは,その型の一部分とはしないので,配列型の一つの変数が異なった長さの配列への参照を含んでもよい。

配列を生成しない配列変数宣言の例を次に示す。

int[] ai;		// array of int
short[][] as;	// array of array of short
Object[] ao,		// array of Object
	otherAo;	// array of Object
short	s,		// scalar short 
	aas[][];	// array of array of short
配列オブジェクトを生成する配列変数の宣言の例を次に示す。

Exception ae[] = new Exception[3]; 
Object aao[][] = new Exception[2][3];
int[] factorial = { 1, 1, 2, 6, 24, 120, 720, 5040 };
char ac[] = { 'n', 'o', 't', ' ', 'a', ' ',
				 'S', 't', 'r', 'i', 'n', 'g' }; 
String[] aas = { "array", "of", "String", };
[]は,宣言の最初の部分における型の一部又は特定の変数に対する宣言子の一部,あるいは,その両方として出現してよい。次に例を示す。

byte[] rowvector, colvector, matrix[];
この宣言は,次と等価とする。

byte rowvector[], colvector[], matrix[][];
一度,配列オブジェクトを生成したら,その長さは不変とする。一つの配列変数が異なる長さの配列を参照するためには,異なる配列への参照を変数へ代入しなければならない。

A を参照型とする場合で,配列変数v が型A[]をもつとき,BA に代入可能ならば,v は任意の配列型B[]のインスタンスへの参照を保持可能とする。これは,その後の代入において実行時例外を生じるかもしれない。詳細については10.10を参照のこと。

10.3 配列生成

配列は,配列生成式(15.10)又は配列初期化子(10.6)によって生成される。

配列生成式は,要素型,入れ子になった配列のレベルの数及び少なくとも一つの入れ子のレベルにおいての配列の長さを指定する。配列の長さは,最終インスタンス変数 length として利用可能とする。

配列初期化子は,配列を生成し,すべての構成要素に初期値を与える。

10.4 配列のアクセス

配列の構成要素は,A[i]のように,配列参照に"["及び"]"で囲ったインデクス式を続けた式で構成される配列アクセス式(15.13)によって,アクセスされる。すべての配列は0を最初の要素とする。長さn の配列は,0からn-1までの整数によってインデクス付け可能とする。

配列は,int値によってインデクス付けしなくてはならない。shortbyte又はchar値もインデクス値として使用してよい。これは,これらが単項数値昇格(5.6.1) に従い,int値となることによる。longインデクス値によって配列構成要素をアクセスしようとすると,コンパイル時エラーが結果として起こる。

すべての配列のアクセスは,実行時に検査される。0未満又は配列の長さ以上のインデクスを使用すると,IndexOutOfBoundsExceptionが投げられる。

10.5 配列:簡単な例題

次に例を示す。

class Gauss {
	public static void main(String[] args) {
		int[] ia = new int[101];
		for (int i = 0; i < ia.length; i++)
			ia[i] = i;
		int sum = 0;
		for (int i = 0; i < ia.length; i++)
			sum += ia[i];
		System.out.println(sum);
	}
}
これは,次の出力を生成する。

5050
この例は,int の配列,つまり int[]という型をもつ変数ia を宣言している。変数 ia は,配列生成式(15.10)によって生成される新たな配列オブジェクトを参照する。配列生成式は,101個の構成要素をもつと指定する。配列の長さは,示されている通り,フィールドlengthを使って利用可能とする。

このプログラムは,配列を 0 から100までの整数で埋め,これらの整数を合計し,その結果を印刷する。

10.6 配列初期化子

配列初期化子(array initializer)は,宣言において又は,配列生成式(15.10)の一部として指定してもよく,配列の生成及び幾つかの初期値設定を行う。

この記述を明確にするために,再度8.3 の記述を次に示す。 配列初期化子は,式をコンマで区切った並びとして記述し,"{"及び"}"で囲む。

構成された配列の長さは,式の数に等しいとする。

配列初期化子における式は,ソースコード中に現れる文字順に左から右に実行される。n番目の変数初期化子は,n-1番目の配列構成要素の値を指定する。 各式は,配列の構成要素型と代入互換(5.2)でなければならない。そうでないときには,コンパイル時エラーが生じる。

構成要素型がそれ自体配列型ならば,構成要素を指定する式は,それ自体,配列初期化子であってよい。つまり,配列初期化子は入れ子にしてもよい。

配列初期化子内における最後の式の後に,それに続いてコンマが出現してもよい。このコンマは無視される。

次に例を示す。

class Test {
	public static void main(String[] args) {
		int ia[][] = { {1, 2}, null };
		for (int i = 0; i < 2; i++)
			for (int j = 0; j < 2; j++)
				System.out.println(ia[i][j]);
	}
}
これは,途中までは,次を出力する。

1
2
ただし,その後,空参照となっている配列 iaの2番目の構成要素にインデクス付けしようとして,NullPointerExceptionが発生する。

10.7 配列のメンバ

配列型のメンバは,次のすべてとする。

これによって,配列は次のクラスと同じ公開フィールドとメソッドをもつ。

class A implements Cloneable, java.io.Serializable {
	public final int length = X;
	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			throw new InternalError(e.getMessage());
		}
	}
}
すべての配列は,インタフェースCloneable及びjava.io.Serializableを実装する。

配列がクローン可能なことは,次のプログラムで示される。

class Test {
	public static void main(String[] args) {
		int ia1[] = { 1, 2 };
		int ia2[] = (int[])ia1.clone();
		System.out.print((ia1 == ia2) + " ");
		ia1[1]++;
		System.out.println(ia2[1]);
	}
}
これは次を出力する。

false 2
これは,ia1及びia2によって参照される配列の構成要素は,異なった変数であることを示す。(初期のJavaプログラム言語の一部の実装において,この例はコンパイルに失敗した。なぜならばコンパイラは,配列のためのクローンメソッドは,CloneNotSupportedExceptionを投げることができると誤って信じていたからである。)

多次元配列のcloneは浅い。すなわち,レベル一つの新しい配列だけを生成する。下位配列は,共有される。

次の例プログラムによって示される。

class Test {
	public static void main(String[] args) throws Throwable {
		int ia[][] = { { 1 , 2}, null };
		int ja[][] = (int[][])ia.clone();
		System.out.print((ia == ja) + " ");
		System.out.println(ia[0] == ja[0] && ia[1] == ja[1]);
	}
}
これは次を出力する。

false true
ia[0] の配列 int[] 及び ja[0] の配列 int[] は,同じ配列となっている。

10.8 配列に対するオブジェクト Class

すべての配列は,関連するClassオブジェクトをもち,このオブジェクトを同じ構成要素型をもつすべての他の配列と共有する。配列型の直接的上位クラスは,Objectとする。すべての配列型は,インタフェースCloneable及びjava.io.Serializableを実装する。

次の例プログラムによって示される。

class Test {
	public static void main(String[] args) {
		int[] ia = new int[3];
		System.out.println(ia.getClass());
		System.out.println(ia.getClass().getSuperclass());
	}
}
これは次を出力する。

class [I
class java.lang.Object
ここで,文字列"[I"は,クラスオブジェクト"構成要素型int をもつ配列"に対する,実行時の型シグネチャとする。

10.9 非文字列としての文字の配列

Cと違い,Javaプログラム言語では,char の配列は Stringでなく,'\u0000'(NUL文字)で終わるchar配列も Stringではない。

charの配列は,変化する要素をもつが,オブジェクトStringは不変であって,その内容は決して変化しない。クラスString 内のメソッドtoCharArrayは,Stringと同じ文字の並びを含む文字配列を返す。クラスStringBufferは,文字の変化可能な配列についての役に立つメソッドを実装する。

10.10 配列記憶例外

A が参照型の場合で,配列変数v が型A[]をもつとき,BA に代入可能ならば,vは任意の配列型B[]のインスタンスへの参照を保持することができる。

次に例を示す。

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
	public static void main(String[] args) {
		ColoredPoint[] cpa = new ColoredPoint[10];
		Point[] pa = cpa;
		System.out.println(pa[1] == null);
		try {
			pa[0] = new Point();
		} catch (ArrayStoreException e) {
			System.out.println(e);
		}
	}
}
これは次の出力を生成する。

true
java.lang.ArrayStoreException
ここで変数 pa は,型 Point[]をもち,変数cpa は,値として型ColoredPoint[]のオブジェクトへの参照値をもつ。ColoredPointは,Pointに代入可能とする。その結果,cpaの値はpaに代入可能とする。

この配列 pa への参照は,例えば,pa[1]nullかどうかを試験することで,実行時型エラーを生じない。これは,型ColoredPoint[]の配列の要素が ColoredPointであって,PointColoredPointの上位クラスなので,すべてのColoredPointPointの代わりとなることが可能であることによる。

一方,配列 paへの代入は,実行時エラーを生じることがある。コンパイル時には,paの要素への代入は,代入された値が確かにPointであることを検査する。しかし,paColoredPointの配列への参照を保持しているので,実行時に代入された値の型がColoredPointであるときだけ,代入は有効とする。

Java仮想計算機は,代入が有効であることを保証するために,実行時にこのような状況を検査する。そうでないときには,ArrayStoreExceptionが投げられる。より形式的には次のとおりとする。つまり,Aを参照型とする場合に,型をA[]とする配列の要素への代入は,実際の要素型がAに代入可能な任意の参照型であってよいとき,代入された値が,確かにその配列の実際の要素型へ代入可能なことを保証するために実行時に検査する。

目次 | | | 索引 Java言語規定
第2版