第10章 配列

目次

10.1 配列タイプ
10.2 配列変数
10.3 配列の作成
10.4 アレイ・アクセス
10.5 配列ストア例外
10.6 配列初期化子
10.7 配列メンバー
10.8 Class配列のオブジェクト
10.9 文字の配列はStringではありません

Javaプログラミング言語では、配列はオブジェクト(§4.3.1)であり、動的に作成され、Object型の変数(§4.3.2)に割り当てることができます。 Objectクラスのすべてのメソッドを配列で呼び出すことができます。

配列オブジェクトには、多数の変数が含まれます。 変数の数はゼロの場合があり、その場合、配列はであるとみなされます。 配列に含まれる変数には名前はありません。かわりに、負でない整数索引値を使用する配列アクセス式によって参照されます。 これらの変数は、配列のコンポーネントと呼ばれます。 配列にn個のコンポーネントがある場合、nは配列の長さであり、配列のコンポーネントは0からn - 1までの整数索引を使用して参照されます。

配列のすべてのコンポーネントは、配列のコンポーネント・タイプと呼ばれる同じ型を持ちます。 配列のコンポーネント・タイプがTの場合、配列自体の型はT[]と書き込まれます。

配列のコンポーネント型は、それ自体が配列型である場合があります。 このような配列のコンポーネントには、サブ配列への参照を含めることができます。 配列型から始めて、そのコンポーネント型が考慮され、(配列型でもある場合)その型のコンポーネント型が考慮され、最終的にコンポーネントに到達する必要がある場合配列型ではない型。これは元の配列の要素型と呼ばれ、データ構造のこのレベルのコンポーネントは元の配列の要素と呼ばれます。

配列の要素を配列にできる状況がいくつかあります。要素タイプがObjectCloneableまたはjava.io.Serializableの場合、これらの型の任意の変数に配列オブジェクトを割り当てることができるため、要素の一部またはすべてを配列にできます。

10.1.  配列タイプ

配列型は、宣言およびキャスト式で使用されます(§15.16)。

配列型は、要素型の名前と、それに続くいくつかの空の角カッコ[]のペアとして記述されます。 大カッコのペアの数は、配列のネスト深度を示します。

配列型内の各大カッコのペアは、型注釈(§9.7.4)で注釈を付けることができます。 アノテーションは、その後に続くカッコのペア(または可変引数パラメータ宣言内の省略記号)に適用されます。

配列の要素型は、プリミティブ型でも参照型でも、任意の型です。 具体的には、次のとおりです。

  • エレメント タイプとしてインターフェイス タイプを持つアレイを使用できます。

    このような配列の要素は、その値としてnull参照またはインタフェースを実装する任意の型のインスタンスを持つことができます。

  • abstractクラス型が要素型である配列を使用できます。

    このような配列の要素は、その値としてNULL参照、またはそれ自体がabstractではないabstractクラスの任意のサブクラスのインスタンスを持つことができます。

配列の長さがその型の一部ではありません。

配列型のスーパータイプは、§4.10.3で指定されています。

配列型のスーパータイプ関係は、スーパークラス関係とは異なります。 Integer[]の直接スーパータイプは、§4.10.3に従ってNumber[]ですが、Integer[]の直接スーパークラスは、Integer[]Classオブジェクト(§10.8)に従ってObjectです。 Objectもすべての配列型のスーパータイプであるため、これは実際には問題ではありません。

10.2.  配列変数

配列型の変数は、オブジェクトへの参照を保持します。 配列型の変数を宣言しても、配列オブジェクトは作成されず、配列コンポーネントに領域が割り当てられません。 これは、配列への参照を含めることができる変数自体のみを作成します。 ただし、宣言子のイニシャライザ部分(§8.3§9.3§14.4.1)は配列を作成でき、その参照が変数の初期値になります。

例10.2-1.  配列変数の宣言


int[]     ai;        // array of int
short[][] as;        // array of array of short
short     s,         // scalar short
          aas[][];   // array of array of short
Object[]  ao,        // array of Object
          otherAo;   // array of Object
Collection<?>[] ca;  // array of Collection of unknown type

前述の宣言では、配列オブジェクトは作成されません。 次に、配列オブジェクトを作成する配列変数の宣言の例を示します:


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", };


変数の配列タイプは、変数宣言の先頭に型の一部として表示されるか、変数の宣言子の一部として表示されるか、またはその両方として表示される可能性のある大カッコのペアによって異なります。 具体的には、フィールド、仮パラメータ、ローカル変数またはレコード・コンポーネントの宣言(§8.3§8.4.1§9.3§9.4§14.4.1§14.14.2§15.27.1§8.10.1)では、変数の配列タイプは次によって示されます。

  • 宣言の開始時に表示される要素タイプ。

  • 宣言子内の変数の識別子に続くカッコのペア(可変引数パラメータまたはレコード・コンポーネントには適用されません)。次に、

  • 宣言の先頭にある型内に出現するカッコのペア(可変個引数パラメータまたは可変個引数レコード・コンポーネントの省略記号はカッコのペアとして扱われます)。

メソッドの戻り型(§8.4.5)は配列型です。 正確な配列タイプは、メソッド宣言の先頭またはメソッドの仮パラメータ・リストの後、あるいはその両方で、型の一部として表示される可能性のあるブラケットのペアによって異なります。 配列タイプは次で示されます:

  • 「結果」に表示される要素タイプ。

  • 仮パラメータのリストの後に続く任意の大カッコのペア。

  • 「結果」に表示されるカッコのペア。

配列変数宣言では、"混合表記法"を使用することはお薦めしません。この宣言では、型と宣言子の両方に大カッコのペアが表示されますが、仮パラメータ・リストの前後に大カッコのペアが表示されるためです。

例10.2-2.  配列変数および配列型

ローカル変数宣言文:

byte[] rowvector, colvector, matrix[];

は次と同等です。

byte rowvector[], colvector[], matrix[][];

各局所変数の配列型は変更されないためです。 同様に、ローカル変数宣言文は次のようになります:

int a, b[], c[][];

次の宣言文のシリーズと同じです:

int a;
int[] b;
int[][] c;

大カッコは、CおよびC++++の潜在的差異の対象として宣言者で使用できます。 一方、変数宣言の一般規則では、タイプと宣言の両方にカッコが表示されるため、ローカル変数宣言文は次のようになります:

float[][] f[][], g[][][], h[];  // Yechh!

次の一連の宣言と同等です:

float[][][][] f;
float[][][][][] g;
float[][][] h;

配列型の形式は、次のパラメータ宣言で同じ配列型を使用します:

void m(int @A [] @B []  x) {}
void n(int @A [] @B ... y) {}

この場合、次のフィールド宣言は配列タイプが同じであると考えられます:

int @A [] f @B [];
int @B [] @A [] g;

配列オブジェクトを作成しても、その長さは変わりません。 配列変数が異なる長さの配列を参照するようにするには、異なる配列への参照を変数に割り当てる必要があります。

配列の長さは型の一部ではないため、配列型の1つの変数に、異なる長さの配列への参照が含まれる場合があります。

配列変数vの型がA[] (Aは参照型)の場合、BA (§5.2)に割り当てることができれば、vは任意の配列型B[]のインスタンスへの参照を保持できます。 これにより、後で割当ての実行時例外が発生する可能性があります。詳細は、§10.5を参照してください。

10.3. 配列の作成

配列は、配列作成式(§15.10.1)または配列イニシャライザ(§10.6)によって作成されます。

配列作成式は、要素タイプ、ネストされた配列のレベル数、およびネストするレベルの少なくとも1つに対する配列の長さを指定します。 配列の長さは、finalインスタンス変数lengthとして使用できます。

配列イニシャライザは、配列を作成し、そのすべてのコンポーネントの初期値を提供します。

10.4.  配列アクセス

配列のコンポーネントは、A[i]のように、値が配列参照であり、その後に[および]で囲まれた索引付け式が続く式で構成される配列アクセス式(§15.10.3)によってアクセスされます。

すべての配列は0-originです。 長さがnの配列は、整数0からn-1で索引付けできます。

例10.4-1.  配列アクセス

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 e : ia) sum += e;
        System.out.println(sum);
    }
}

このプログラムは出力を生成します:

5050

このプログラムは、intの型配列を持つ変数ia、つまりint[]を宣言します。 変数iaは、配列作成式(§15.10.1)によって作成された、新しく作成された配列オブジェクトを参照するように初期化されます。 配列作成式は、配列に101コンポーネントを含めることを指定します。 配列の長さは、次に示すように、フィールドlengthを使用して使用できます。 プログラムは、配列に0から100までの整数を入力し、これらの整数を合計して結果を出力します。


配列は、int値で索引付けする必要があります。shortbyteまたはchar値は、単項数値昇格(§5.6)の対象となり、int値になるため、索引値としても使用できます。

long索引値を使用して配列コンポーネントにアクセスしようとすると、コンパイル時にエラーが発生します。

すべての配列アクセスは実行時にチェックされます。0未満または配列の長さ以上の索引を使用しようとすると、ArrayIndexOutOfBoundsExceptionがスローされます(§15.10.4)。

10.5. 配列ストア例外

型がA[](Aは参照型)の配列の場合、配列のコンポーネントへの割当てが実行時にチェックされ、割り当てられる値がコンポーネントに割り当て可能であることが確認されます。

割り当てられる値の型がコンポーネント型との代入互換性がない(§5.2)場合、ArrayStoreExceptionがスローされます。

配列のコンポーネント・タイプが再可能でない場合(§4.7)、Java Virtual Machineは、前の段落で説明したストア・チェックを実行できませんでした。 このため、変更不可能な要素型を持つ配列作成式は禁止されています(§15.10.1)。 要素型が再可能でない配列型の変数を宣言することもできますが、配列作成式の結果を変数に代入すると、必ずしも未チェックの警告が発生します(§5.1.9)。

例10.5-1.  ArrayStoreException

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: Point

変数paPoint[]型を持ち、変数cpaはその値としてColoredPoint[]型のオブジェクトへの参照を持ちます。 ColoredPointPointに割り当てることができるため、cpaの値をpaに割り当てることができます。

この配列paへの参照。たとえば、pa[1]nullかどうかをテストしても、実行時型エラーは発生しません。 これは、ColoredPoint[]型の配列の要素が ColoredPointであり、PointColoredPointのスーパークラスであるため、すべてのColoredPointPointを表すことができるためです。

一方、配列paへの割当てにより、ランタイム・エラーが発生する可能性があります。 コンパイル時に、paの要素への割当てがチェックされ、割り当てられた値がPointであることが確認されます。 ただし、paColoredPointの配列への参照を保持しているため、実行時に割り当てられた値の型がColoredPointの場合にのみ、この割当てが有効になります。

Java Virtual Machineは、実行時にこのような状況をチェックして、割当てが有効であることを確認します。有効でない場合は、ArrayStoreExceptionがスローされます。


10.6. 配列初期化子

配列イニシャライザは、配列を作成して初期値を提供するために、フィールド宣言(§8.3§9.3)またはローカル変数宣言(§14.4)で指定することも、配列作成式(§15.10.1)の一部として指定することもできます。

ArrayInitializer:
VariableInitializerList:

ここでは、便宜上、§8.3の次の本番環境を示します。

VariableInitializer:

配列イニシャライザは、カンマ区切りの式のリストとして記述され、中カッコ{および}で囲まれます。

配列イニシャライザの最後の式の後に末尾のカンマを指定でき、無視されます。

各変数イニシャライザは、配列のコンポーネント型と代入互換(§5.2)である必要があります。そうでないと、コンパイル時にエラーが発生します。

初期化される配列のコンポーネント・タイプが再可能でない場合(§4.7)、コンパイル時にエラーが発生します。

構築される配列の長さは、配列イニシャライザの中カッコで囲まれた変数イニシャライザの数と等しくなります。 領域は、その長さの新しい配列に割り当てられます。 配列を割り当てるための十分な領域がない場合、配列イニシャライザの評価は、OutOfMemoryErrorをスローして突然完了します。 それ以外の場合は、指定された長さの1次元配列が作成され、配列の各コンポーネントがデフォルト値(§4.12.5)に初期化されます。

配列イニシャライザの中カッコで囲まれた変数イニシャライザは、ソース・コードで出現するテキスト順で左から右に実行されます。 n番目の変数イニシャライザは、n-1番目の配列コンポーネントの値を指定します。 変数イニシャライザの実行が突然完了すると、配列イニシャライザの実行も同じ理由で突然完了します。 すべての変数イニシャライザ式が正常に完了すると、配列イニシャライザは、新しく初期化した配列の値で正常に完了します。

コンポーネント・タイプが配列型の場合、コンポーネントを指定する変数イニシャライザ自体が配列イニシャライザである可能性があります。つまり、配列イニシャライザはネストできます。 この場合、ネストした配列イニシャライザを実行すると、前述のアルゴリズムの再帰的適用によって配列オブジェクトが構築および初期化され、コンポーネントに割り当てられます。

例10.6-1. 配列イニシャライザ

class Test {
    public static void main(String[] args) {
        int[][] ia = { { 1, 2 }, null };
        for (int[] ea : ia) {
            for (int e: ea) {
                System.out.println(e);
            }
        }
    }
}

このプログラムは出力を生成します:

1
2

配列iaの2番目のコンポーネント(null参照)の索引付けを試行する際に、NullPointerExceptionが発生する前に。


10.7.  アレイ・メンバー

配列型のメンバーは、次のすべてです。

  • 配列のコンポーネント数を含むpublic finalフィールドlengthlengthは正またはゼロです。

  • publicメソッドcloneは、クラスObjectの同じ名前のメソッドをオーバーライドし、チェックされた例外をスローしません。 配列型T[]cloneメソッドの戻り型は、T[]です。

    多次元配列のクローンは浅いため、新しい配列は1つのみ作成されます。 サブ配列は共有されます。

  • クラスObjectから継承されるすべてのメンバー。継承されないObjectのメソッドは、そのcloneメソッドのみです。

    Objectpublicメソッドと非publicメソッドの違いが特別な注意を必要とする別の状況については、§9.6.4.4を参照してください。

したがって、配列には、次のクラスと同じpublicフィールドおよびメソッドがあります。


class A<T> implements Cloneable, java.io.Serializable {
    public final int length = X;
    public T[] clone() {
        try {
            return (T[])super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
        }
    }
}

前述のコードでT[]にキャストすると、配列が実際にこの方法で実装された場合、未チェックの警告(§5.1.9)が生成されることに注意してください。

例10.7-1. クローン可能なアレイ

class Test1 {
    public static void main(String[] args) {
        int[] ia1 = { 1, 2 };
        int[] ia2 = ia1.clone();
        System.out.print((ia1 == ia2) + " ");
        ia1[1]++;
        System.out.println(ia2[1]);
    }
}

このプログラムは出力を生成します:

false 2

ia1およびia2によって参照される配列のコンポーネントが異なる変数であることを示す。


例10.7-2. クローン作成後の共有サブアレイ

多次元配列の複製時にサブ配列が共有されるという事実は、次のプログラムによって示されます。

class Test2 {
    public static void main(String[] args) throws Throwable {
        int[][] ia = { { 1, 2 }, null };
        int[][] ja = 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オブジェクトがあり、同じコンポーネント・タイプを持つ他のすべての配列と共有されます。

配列型はクラスではありませんが、すべての配列のClassオブジェクトは、次のように動作します。

  • すべての配列型の直接スーパークラスはObjectです。

  • すべての配列型は、インタフェースCloneableおよびjava.io.Serializableを実装します。

例10.8-1. Class配列のオブジェクト

class Test1 {
    public static void main(String[] args) {
        int[] ia = new int[3];
        System.out.println(ia.getClass());
        System.out.println(ia.getClass().getSuperclass());
        for (Class<?> c : ia.getClass().getInterfaces())
            System.out.println("Superinterface: " + c);
    }
}

このプログラムは出力を生成します:

class [I
class java.lang.Object
Superinterface: interface java.lang.Cloneable
Superinterface: interface java.io.Serializable

ここで、文字列"[I"は、Classオブジェクト"array with component type int"の実行時型のシグネチャです。


例10.8-2. 共有される配列Classオブジェクト

class Test2 {
    public static void main(String[] args) {
        int[] ia = new int[3];
        int[] ib = new int[6];
        System.out.println(ia == ib);
        System.out.println(ia.getClass() == ib.getClass());
    }
}

このプログラムは出力を生成します:

false
true

iaibは異なる配列を参照しますが、Classオブジェクトの比較の結果は、int型のコンポーネントを持つすべての配列が同じ配列型(つまり、int[])のインスタンスであることを示しています。


10.9. 文字の配列はStringではありません

Javaプログラミング言語では、Cとは異なり、charの配列はStringではなく、Stringcharの配列も'\\u0000' (NUL文字)で終了しません。

Stringオブジェクトは不変です。つまり、その内容は変更されませんが、charの配列には可変要素があります。

クラスStringのメソッドtoCharArrayは、Stringと同じ文字シーケンスを含む文字の配列を返します。 クラスStringBufferは、可変文字配列に対して有用なメソッドを実装します。