|
|
配列 |
H.Kamifuji . |
同じ目的使用される複数の値を格納するのに便利なのが配列です。 プログラムの中で値を一時的に保存しておきたい場合、変数を使うことで可能です。ただ例えば生徒数十人のテストの点数を保存するために変数を生徒の数だけ用意しようとするとプログラムが不必要に分かりにくくなります。また保存した値の平均を計算しようとした場合に、変数を全て一つずつ加算して人数で割るといった記述が必要となります。 配列は変数に似ていますが、変数が一つの値を保管するのに対して配列では複数の値を保管することができます。配列を利用することで、似たような名前の変数を大量に作らなくても済み、また配列内の複数の記憶場所に格納された値を順番に処理するといったことが可能になります。 ここではJavaにおける配列の使い方について確認していきます。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
まず配列の使い方について簡単に確認します。 例として5人分の成績を変数に格納する場合を考えてみます。 int result1, result2, result3, result4, result5; result1 = 85; result2 = 78; result3 = 92; result4 = 62; result5 = 69;変数は1つにつき1つの値しか格納できませんので5人分の成績を格納するには5つの変数が必要です。 では続けて変数に格納した値を順に画面に表示してみます。 int result1, result2, result3, result4, result5; result1 = 85; result2 = 78; result3 = 92; result4 = 62; result5 = 69; System.out.println(result1); System.out.println(result2); System.out.println(result3); System.out.println(result4); System.out.println(result5);変数の場合、一つ一つ処理するしかありません。5人であればまだ大丈夫ですが、これが50人分となるとプログラムが似たような文が羅列され長々としたものになってしまいます。 このように同じデータ型を使い同じような目的で利用される変数を数多く必要とする時には配列を使うと便利です。例えば次のように使います。 int result[] = new int[5]; result[0] = 85; result[1] = 78; result[2] = 92; result[3] = 62; result[4] = 69;配列では配列変数と呼ばれる変数を一つ用意します。そして変数とは異なり配列では値を格納する場所を任意の数だけ持つことができます。まったく利用目的が異なる複数の変数を配列でまとめて管理するのは逆に分かりにくくなりますが、同じような目的の値を複数格納するために少しだけ名前が違う変数を多数用意するような場合は一つの配列でまとめて管理した方が分かりやすくなります。 また配列の大きなメリットは繰り返し処理と組み合わせて利用できることです。配列に格納された値を順に出力する場合は次のように記述することができます。 int result[] = new int[5]; result[0] = 85; result[1] = 78; result[2] = 92; result[3] = 62; result[4] = 69; for (int i = 0; i < 5; i++){ System.out.println(result[i]); }さらに初期値の代入を次のように記述することもできます。 int result[] = {85, 78, 92, 62, 69}; for (int i = 0; i < 5; i++){ System.out.println(result[i]); }かなり簡潔に記述することができるようになりました。このように配列をうまく使うことで同じような目的の値を数多く使う場合に簡潔に記述することができるようになります。 では次のページ以降で詳しく使い方を見ていきます。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample1_1{ public static void main(String args[]){ int result[] = {85, 78, 92, 62, 69}; for (int i = 0; i < 5; i++){ System.out.println(result[i]); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample1_1 85 78 92 62 69 [xxxxxxxx@dddddddddd Array]$ |
配列を使用するには、まず最初に使用する配列を宣言しなくてはいけません。宣言は次のように行います。 型名 配列変数名[];変数の場合と同じく格納される値のデータ型と、配列を区別するための配列変数名を指定します。一つの配列には同じデータ型の値しか格納することはできません。(配列変数名の付け方については変数名の場合と同じです。詳しくは「変数名のつけ方(識別子)」を参照して下さい)。 変数の場合は宣言するだけで一つの値を格納できる場所が確保されましたが、配列の場合では値を格納する場所を必要な数だけ後から確保する必要があります。場所を確保するにはnew演算子を使って次のように行います。 型名 配列変数名[]; 配列変数名 = new 型名[要素数];一つの配列に確保される一つ一つの場所を要素と呼びます。new演算子では型名の後の「[」から「]」の間に要素の数である要素数を指定して格納する場所である要素を確保し、それを配列変数に代入します。これで配列は要素数の数だけの値を格納することができるようになります。 なお配列の宣言と要素の確保はまとめて次のように記述することもできます。 型名 配列変数名[] = new 型名[要素数];例えばint型の値を5つ格納できる配列を用意するには次のように記述します。 int a[]; a = new int[5]; int b[] = new int[5];上は配列の宣言と要素の確保を別々に行った場合、下は要素の宣言と配列の確保をまとめて行った場合です。 配列の宣言の別の方法Javaでは先に記述した記述方法の他に次の書式を使うことができます。型名[] 配列変数名;どちらの書式であっても配列の宣言に違いはありません。どちらかの方法で統一して使うようにしていればいいと思います。ただどちらかの書式では記述しにくい時もありますので二つの記述方法があることは覚えておいて下さい。 この書式を使ってint型の値を5つ格納できる配列を用意するには次のように記述します。 int[] a; a = new int[5]; int[] b = new int[5];では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample2_1{ public static void main(String args[]){ char[] moji; moji = new char[2]; moji[0] = 'A'; moji[1] = '漢'; for (int i = 0; i < 2; i++){ System.out.println(moji[i]); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample2_1 A 漢 [xxxxxxxx@dddddddddd Array]$ |
配列変数は参照型と呼ばれる変数で基本のデータ型を使った変数とは少し取り扱い方が異なります。 基本データ型の変数の場合は、変数は値を格納する入れ物そのものです。よって変数と変数に格納されている値は同じように扱うことができます。それに対して配列のような参照型の変数では値を格納する入れ物は別に用意されます。そして参照型の変数には入れ物の位置が格納されることになります。 では配列を宣言してみます。 int n[];配列変数には入れ物の位置が格納されますが、宣言しただけではまだ入れ物が用意されていません。よってこの時点ではnullという特別な値が配列変数に格納されています。nullはこのように参照型の変数にまだ適切な値が格納されていないことを表すために用意された定数です。 次にnew演算子を使って必要な数の要素を確保します。 int n[]; n = new int[5];new演算子を使って5個の値が格納できる場所が確保されると同時に、確保された場所の先頭の場所の位置が配列変数に代入されます。この時点で初めて配列変数は有効な値を持つことになります。 このように配列変数そのものはあくまでどこかに確保された場所の位置だけを格納しています。配列に値を格納したり、値を取り出したりする場合は配列変数そのものではなく配列の各要素に対して行うことになります。(配列の要素の使い方は次のページで解説します)。 参照型で出てくる場所の位置という考え方はCやC++などのアドレスに似ていますが、Cのポインタのように直接操作することはできません。 参照型変数の値を代入参照型の変数にはどこかに確保された場所の位置が代入されていますが、別に用意した参照型の変数に代入することもできます。次の例を見てください。 下記のサンプルを実行してみよう。 int n[] = new int[3]; int m[]; m = n;配列変数「m」に配列変数「n」を代入しています。この結果、どちらの配列変数も同じ場所の位置を持つことになります。このため、どちらの配列変数を使っても同じ要素に対して操作することができます。 通常の変数の場合には、元の変数の値が複製されて代入先の変数に格納されますが、配列を別の配列にコピーした場合は、元の配列が持っていた要素が複製されるわけではなく、まったく同じ要素に対して二つの配列を変数を使ってアクセスすることができるようになります。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample3_1{ public static void main(String args[]){ int n[]; int m[]; n = new int[2]; System.out.println(n); n[0] = 10; m = n; System.out.println(m); System.out.println("n[0] = " + n[0]); System.out.println("m[0] = " + m[0]); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample3_1 [I@15db9742 [I@15db9742 n[0] = 10 m[0] = 10 [xxxxxxxx@dddddddddd Array]$今回は要素を確保した後の配列変数の値を画面に出力させてみました。この値そのものは特に利用できるものではありません。また別の配列変数を用意して代入しています。どちらの配列変数にも同じ要素の位置を持っていますので、どちらの配列変数を使っても同じ要素を参照することができます。 |
配列は複数の値を格納する場所を管理でき、その一つ一つは要素と呼ばれます。要素は通常の変数と同じように値を格納するための入れ物であり、直接値を代入したり取得したりすることが可能です。 配列の各要素は次のように表すことができます。 配列変数[インデックス]配列変数の後の「[」と「]」の間にインデックス(又は添え字)と呼ばれる整数を記述することで特定の要素を表すことができます。配列では複数の要素を持つことができるため、何番目の要素かを特定するのがインデックスで指定される数値だと考えて下さい。 例えば5個の要素を持つ配列を作成した場合にはインデックスとして0から4までを使用して各要素を表すことができます。1から5ではなく0から始まることに注意して下さい。 実際には次のように記述します。 int n[] = new int[5]; n[0] = 10; n[1] = 14; n[2] = 24; n[3] = 18; n[4] = 22; System.out.println(n[0]); int sum = n[1] + n[2]; n[4] = n[3] * 5;配列変数自体は参照型の変数でしたが、配列に含まれる各要素は普通の変数と同じように値を代入することができます。また、プログラムの中で要素を記述すると要素に格納されている値が記述されているように処理されます。このように配列の要素は通常の変数と同じように扱うことができます。 不正なインデックスインデックスを指定する場合には、その対象となる配列で確保された要素数の範囲内でなければなりません。例えば要素の数を5個しか確保していないのにインデックスに7などの数値を指定すると実行時エラーとなります。int n[]; n = new int[5]; n[7] = 15;上記はコンパイルの時点ではエラーとなりませんが、実際に実行してみると「java.lang.ArrayIndexOutOfBoundsException」と言うエラーが発生します。Javaでは事前に用意した要素の数しか利用できませんので注意して下さい。 インデックスを変数で指定インデックスに指定する値は整数(ただしlong型の値は指定できません)ですので、数値を直接記述する以外に整数型の変数を指定することもできます。int n[] = new int[5]; int index = 0; n[index] = 15;変数を使ってインデックスを指定できることで、繰り返し処理と配列を組み合わせて効率的に配列に含まれる各要素を処理することが可能になります。次のような使い方は頻繁に使用されるので覚えておいて下さい。 int n[] = new int[3]; n[0] = 10; n[1] = 20; n[2] = 18; for (int i = 0; i < 3; i++){ System.out.println(n[i]); }インデックスの値をfor文などを使って変化させ、配列に含まれる全ての要素を順に処理しています。このような使い方ができることが配列で複数の値を管理する大きなメリットとなります。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample4_1{ public static void main(String args[]){ int n[] = new int[3]; n[0] = 20; n[1] = 18; n[2] = n[0] * 2 + n[1]; System.out.println(n[2]); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample4_1 58 [xxxxxxxx@dddddddddd Array]$ |
変数では次のように宣言と同時に初期値を代入することができました。 型名 変数名 = 初期値;配列の場合で配列の宣言と要素の確保、そして各要素への初期値の代入をまとめて行うには次の書式を使います。 型名 配列変数名[] = {値1, 値2, ... };配列の各要素に代入する初期値を「{」から「}」の間にカンマ(,)で区切って記述します。要素の確保が行われていませんが、値を記述した数と同じ要素を自動的に確保します。 実際には次のように記述します。 int n[] = {18, 29, 36, 12};この場合、int型のデータを扱い4つの要素を持つ配列を作成し、各要素には「18」「29」「36」「12」と言う値を代入するということになります。これは次のように記述した事と同じです。 int n[]; n = new int[4]; n[0] = 18; n[1] = 29; n[2] = 36; n[3] = 12;この書式はとても便利ですが、配列の要素数の分だけ必ず値を指定する必要があります。8個の要素が必要だが初期化の時点では4つ要素だけ初期値を代入したいといった場合にはこの書式ではなく配列の宣言を行って要素を確保してから一つ一つ初期値を代入していくしかありません。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample5_1{ public static void main(String args[]){ int n[] = {18, 29, 36, 12}; for (int i = 0; i < 4; i++){ System.out.println(n[i]); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample5_1 18 29 36 12 [xxxxxxxx@dddddddddd Array]$ |
配列で確保されている要素の数を調べたい場合があります。要素の数は配列の長さとも呼ばれますが次の書式で取得することができます。 配列変数名.length調べたい配列変数の後にドット(.)+lengthと記述することで配列の長さを取得できます。(なおlengthはフィールドではなく特別な構文です。フィールドについてはクラスの説明の時に行います)。 実際には次のように記述します。 int n[] = {18, 29, 36, 12}; System.out.println(n.length);配列には4つの要素が確保されていますので画面には4と表示されます。 配列の長さは、配列と繰り返し処理を組み合わせて使用する場合によく使用されます。配列の全ての要素を順に処理したい時、要素の数を取得して繰り返しを行う回数を決めます。次の例を見てください。 int n[] = {18, 29, 36, 12}; for (int i = 0; i < n.length; i++){ System.out.println(n[i]); }条件式の箇所で「配列変数.length」を使い繰り返し回数を決めています。配列の要素数が確定していれば数値を直接記述した方が効率がいいのですが、配列変数.lengthを使っておけば後で配列の要素の数が変更になった場合でも修正は必要ありません。また、数値が記述されているとなぜこの数値なのか後から分からなくなる可能性もありますが、数値の代わりに配列変数.lengthが記述されていれば配列の長さだけ処理したかっということがはっきりと分かります。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample6_1{ public static void main(String args[]){ int n[] = {18, 29, 36, 12}; for (int i = 0; i < n.length; i++){ System.out.println(n[i]); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample6_1 18 29 36 12 [xxxxxxxx@dddddddddd Array]$ |
配列は複数の値をまとめて管理できる便利なものですが、配列の各要素に代入する値に別の配列を使うことができます。 次の例を見てください。 int kokugo[] = {80, 92, 45}; int sansu[] = {75, 89, 54};国語と算数の成績をそれぞれ3人分管理しています。3人分の国語の成績を管理するの配列を一つ利用し、3人分の算数の成績を管理するのに配列を1つ利用しています。 ここでこの二つの配列を値として考え、配列を値として格納するような別の配列を用意します。配列の宣言は次のようになっていました。 型名 配列変数名[];作成しようとする配列に格納されるデータ型はint型の値を持つ配列です。つまりデータ型はint[]となります。よって配列を値として持つ配列は次のように記述することができます。 int[] seiseki[];この配列は名前が「seiseki」で型名がint[]です。ただこの記述方法でもエラーとはなりませんが、通常は次のいずれかの記述方法を使います。 int seiseki[][]; int[][] seiseki;この配列変数は値として配列を格納する配列です。この配列変数を使うことで3人分の成績を格納した配列を値として持つことができます。このような配列は多次元配列と呼ばれるます。 多次元配列の要素の確保多次元配列であっても配列と違いはないので利用する前に要素の確保が必要であり次のように記述します。型名 配列変数名[][]; 配列変数名 = new 型名[要素数][];例えば2つの要素を持つ場合には次のようになります。 int seiseki[][]; seiseki = new int[2][];これで多次元配列の要素が確保されました。次に要素に値を格納していきますが、値として格納するのは別の配列です。例えば次のように記述します。 int seiseki[][]; seiseki = new int[2][]; int kokugo[]; kokugo = new int[3]; kokugo[0] = 80; kokugo[1] = 92; kokugo[2] = 45; int sansu[]; sansu = new int[3]; sansu[0] = 75; sansu[1] = 89; sansu[2] = 54; seiseki[0] = kokugo; seiseki[1] = sansu;丁寧に記述するとこのようになりますが、多次元代入に値として代入する配列を別に作らなくても次のように記述することができます。 int seiseki[][]; seiseki = new int[2][]; seiseki[0] = new int[3]; seiseki[1] = new int[3]; seiseki[0][0] = 80; seiseki[0][1] = 92; seiseki[0][2] = 45; seiseki[1][0] = 75; seiseki[1][1] = 89; seiseki[1][2] = 54;慣れないと分かりにくいと思いますがseiseki[0]をkokugoにseiseki[1]をsansuに置き換えてみて頂ければ分かると思います。 さらにまとめて次のように記述することができます。 int seiseki[][]; seiseki = new int[2][3]; seiseki[0][0] = 80; seiseki[0][1] = 92; seiseki[0][2] = 45; seiseki[1][0] = 75; seiseki[1][1] = 89; seiseki[1][2] = 54;この場合、int型の値を持つことができる要素を3つ分確保した配列を値として持つことができる要素を2つ確保した配列を作成します。結局のところ多次元配列における要素の確保は次のように記述できます。 型名 配列変数名[][]; 配列変数名 = new 型名[要素数][値の配列の要素数];また通常の配列と同じように配列の宣言と要素の確保はまとめて次のように記述することもできます。 型名 配列変数名[][] = new 型名[要素数][値の配列の要素数];よって先ほどまでのサンプルは次のように記述できます。 int seiseki[][] = new int[2][3]; seiseki[0][0] = 80; seiseki[0][1] = 92; seiseki[0][2] = 45; seiseki[1][0] = 75; seiseki[1][1] = 89; seiseki[1][2] = 54;多次元配列は配列を値として持つような配列であり、複数の変数を使用することで繰り返し処理と組み合わせて多くの値をまとめて処理することができるようになります。 なお今回の多次元配列は2次元の配列でしたが、2次元の配列を値として持つような3次元の配列など何次元の配列でも作成は可能です。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample7_1{ public static void main(String args[]){ int seiseki[][] = new int[2][3]; seiseki[0][0] = 80; seiseki[0][1] = 92; seiseki[0][2] = 45; seiseki[1][0] = 75; seiseki[1][1] = 89; seiseki[1][2] = 54; String kyoka[] = {"国語", "数学"}; for (int i = 0; i < 2; i++){ System.out.println(kyoka[i] + "の成績"); for (int j = 0; j < 3; j++){ System.out.println(seiseki[i][j]); } } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample7_1 国語の成績 80 92 45 数学の成績 75 89 54 [xxxxxxxx@dddddddddd Array]$今回のサンプルのように多次元配列に含まれる値を繰り返し処理をネスト(入れ子にすること)させて処理することはよく使われる方法です。 多次元配列は便利ですが、あまり多次元にすると分かりにくいプログラムになってしまいますので作る場合でも3次元くらいまでにしておくことをお薦めします。 |
配列の各要素に初期値を設定する方法は次のような書式を使いました。 型名 配列変数名[] = {値1, 値2, ..};多次元配列の場合は要素に代入される値が別の配列となりますので次のような書式となります。 型名 配列変数名1[] = {値1, 値2, ..}; 型名 配列変数名2[] = {値1, 値2, ..}; 型名 配列変数名[][] = {配列変数名1, 配列変数名2};別途作成された配列を値のように記述しています。またさらに次のように記述することもできます。 型名 配列変数名[][] = {{値1_1, 値1_2, ..}, {値2_1, 値2_2, ..}};型名 配列変数名[][] = {{値1_1, 値1_2, ..}, {値2_1, 値2_2, ..}}; int num[][] = {{10, 8, 5}, {9, 16, 4}, {3, 7, 5}};通常の配列では値をカンマ(,)で区切って記述していましたが多次元配列の場合は値の場所に別の配列を記述しています。これは次のように記述した場合と同じです。 int num[][] = new int[2][3]; num[0][0] = 10; num[0][1] = 8; num[0][2] = 5; num[1][0] = 9; num[1][1] = 16; num[1][2] = 4; num[2][0] = 3; num[2][1] = 7; num[2][2] = 5;Javaではプログラムの途中で空白や改行を入れることができますので、初期値が長くなった場合には例えば次のように記述すると分かりやすいかもしれません。 int num[][] = { {10, 8, 5}, {9, 16, 4}, {3, 7, 5} };注意点としては通常の配列と同じく必要な要素の分だけ全て値を指定する必要があります。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample8_1{ public static void main(String args[]){ int seiseki[][] = { {68, 82, 92}, {76, 33, 83}, {92, 45, 38} }; String kurasu[] = {"Aクラス", "Bクラス", "Cクラス"}; for (int i = 0; i < 3; i++){ System.out.println(kurasu[i] + "の成績"); for (int j = 0; j < 3; j++){ System.out.println(seiseki[i][j]); } } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample8_1 Aクラスの成績 68 82 92 Bクラスの成績 76 33 83 Cクラスの成績 92 45 38 [xxxxxxxx@dddddddddd Array]$ |
多次元配列の場合も「配列変数名.length」で配列の長さをを取得することができます。 配列変数名.lengthでは多次元配列の場合に取得できる値を確認してみます。次の例を見てください。 int num[][] = {{1, 3, 5}, {2, 4, 6}}; System.out.println(num.length);多次元配列における要素の数なので、この場合は「2」と出力されます。もしも要素として代入されている個々の配列の要素数を取得したい場合には次のように要素に対して「.length」を付けて取得して下さい。 配列変数名[インデックス].length具体的には次のように記述します。 int num[][] = {{1, 3, 5}, {2, 4, 6}}; System.out.println(num[0].length); System.out.println(num[1].length);この場合は多次元配列の要素に代入されている配列の要素数をそれぞれ出力しているため、どちらも「3」と出力されます。 多次元配列では次のページで解説するように要素に代入する配列の長さは同じである必要がないため、各要素に代入されてる配列の長さを個々に取得する必要がある場合はこのように「多次元配列名[インデックス].length」で取得して下さい。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample9_1{ public static void main(String args[]){ int num[][] = {{1, 3, 5}, {2, 4, 6}}; System.out.print("多次元配列の長さは"); System.out.println(num.length); System.out.print("インデックス0の要素の長さは"); System.out.println(num[0].length); System.out.print("インデックス1の要素の長さは"); System.out.println(num[1].length); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Array]$ java JSample9_1 多次元配列の長さは2 インデックス0の要素の長さは3 インデックス1の要素の長さは3 [xxxxxxxx@dddddddddd Array]$ |
|