目次 | |

10. 配列

Javaの 配列(arrays) は,オブジェクト(4.3.1)とし,動的に生成され,型 Object(4.3.2)の変数として代入可能とする。すべてのクラス Object のメソッドは,配列に対して呼出し可能とする。

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

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

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

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

10.1 配列型

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

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

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

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.9) 又は配列初期化子 (10.6)によって生成する。

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

配列初期化子は,配列を生成し,すべての構成要素に初期値を与える。(C及び C++ の場合とを比較すること。C 及び C++では,配列初期化式は,配列の構成要素のすべてではなくて,いくつかに対しても初期値を指定できる。)

10.4 配列アクセス

配列の構成要素は,A[i] のように,配列参照に "["及び "]"で囲ったインデクス式を続けた式で構成される配列アクセス式によって,アクセスされる(15.12)。すべての配列は 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.9)によって生成される新たな配列オブジェクトを参照する。配列生成式は,101個の構成要素をもつとして指定する。配列の長さは,示されている通り,フィールドlength を使って利用可能とする。

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

10.6 配列初期化子

配列初期化子(array initializer)は,宣言において指定可能とし,配列の生成及びいくつかの初期値設定を行う。

この記述を明確にするために,再度 8.3 の記述を次に示す。

配列初期化子は,式をコンマで区切った並びとして記述し,"{"及び"}"で囲む。

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

各式は,一つの配列の構成要素に対する値を指定する。各式は,配列の構成要素型と代入互換(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 {
    public final int length = X;
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
        }
    }
}
すべての配列は,インタフェース Cloneableを実装する。配列がクローン可能なことは,次のプログラムで示される。


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によって参照される配列の構成要素は,異なった変数であることを示す。

多次元配列の 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とする。


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 をもつ配列”に対する,実行時の型シグネチャとする(20.1.1)

10.9 char の配列と String との差異

Cと違い,Javaでは,char の配列は Stringでなく(20.12)'\u0000'(NUL文字)で終わるchar 配列も String ではない。

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

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に代入可能な任意の参照型であってよいとき,代入された値が,確かにその配列の実際の要素型へ代入可能なことを実行時に検査する。


目次 | |