[PR] 迷惑メール トップページプロ3ゼミJavaJava入門講座一学期単位4 →2時間目

2時間目 配列

4−2−1.配列の必要性

 普段私たちが行っている作業又は業務は、大量なデータを扱う事が多く、管理するにはそのデータごとにまと めて管理します。
配列は、データを大量に扱う場合に必要で、プログラムでは効率よくまとめて処理できます。

 プログラム上では、データを代入するのに変数を宣言しなければなりません。
そのため、上記の場合では、大量にデータがある場合(例えば、1万個)その数だけ変数を宣言しなければなり ません。
これでは、労力を使い生産性から言って効率よくありません。
プログラムは、本来利用者の生産性を上げるために開発され、プログラマにとっても開発しやすくなければ本 末転倒です。

 配列は、型であれば配列宣言する事で、変数を使用することが出来ます。
この配列機能を利用する事により、問題であった労力を余計に消費することなく、生産性も上げる事ができます。
これだけでも配列の必要性が見えますね。

4−2−2.配列の宣言と初期化

 配列は、他の言語でも扱える事が多く、宣言や初期化は言語によって様々です。
Javaは、CやC++などを参考に開発された経緯やオブジェクト指向による特徴のある宣言と初期化を行います。
それでは、実際に幾つか例を紹介します。

・配列の宣言
int[] array; // 配列を宣言

 この宣言により、arrayは、int[]型(intの配列型)を使用できます。
ここで注目して欲しいのは、arrayは配列である事を宣言しましたが、幾つデータを代入するかが示されていま せん。
宣言するだけではなく、初期化しなければなりません。

・配列の初期化
array = new int[10]; // 配列の初期化

 これによって、arrayは10個int型を宣言したのと同じです。

・配列の宣言と同時に初期化
int[] array = new int[10]; // 配列の宣言と初期化

 これは、arrayをint[]型で宣言し、10個のint型を扱えるように初期化しました。

 上記の3つは、配列としては、宣言及び初期化しましたが、1つ1つの要素はまだ代入されていません。
ですが、この状態では、要素にアクセスすればすべて0です。
配列の宣言だけで要素にアクセスした場合はコンパイルエラーです。
 代入には2つあります。

・配列の要素へ1つ1つ代入(arrayはint[]型が宣言され5個で初期化されています)
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;

 配列のアクセス方法についてはこの時間の「2−3.配列の要素へのアクセス方法及び添字」で説明します。

・配列の宣言と同時に配列の要素を初期化する
int[] array = new int[]{ 10, 20, 30 }; // 宣言と同時に配列の要素を初期化。配列の長さは3。
int[] array = { 10, 20, 30 }; // 上記方法の簡略版、こちらも配列の長さは3。

 2つ方法がありますが、どちらでも結果は同じです。
オブジェクト指向で考えると1つ目の方が合っていますが、簡略版も一般的に使われています。
ここでnew int[]に整数を入れていないことに注目してください。
ここの中に整数を入れるとコンパイルエラーです。
Javaは、要素の初期化で自動的に配列の長さを調べてくれます。

4−2−3.配列の要素へのアクセス及び添字

 先程、少しですが使用方法を紹介しました。
ここでは詳細に説明します。
配列を宣言し、配列の初期化と配列の要素を初期化することにより配列が使用できます。
プログラム上で、例えば、「int[] array;」とした場合これは何を意味するのでしょうか。
最初に説明したように、これは配列を宣言しました。
正確に言いますと、これは配列の要素を示す場所をメモリ上に確保しただけです。
住所で例えると、1地域で管理する場所を確保しただけです。

 例えば地域別に管理する棚に置き換えます。
要素数を示す事は、棚の部屋数を示す事です。
実際に棚の部屋に格納する作業が配列の要素へ1つ1つ代入または配列の宣言と同時に配列の要素を初期化する事 により、実現しています。
配列の宣言だけの場合は、棚の部屋を確保するだけで棚の部屋にはデータを格納しません。
配列の宣言と同時に配列の要素を初期化では、使用する棚の部屋は決まっている為、部屋の数を決め実際に資 料を格納します。

 次の場合はどうなのかを考えて見ましょう。
すでにarrayには10個で初期化された状態で次が処理されます。
「array = new int[20];」
配列の宣言はあくまでも要素を示す場所をメモリ上に確保しただけなので、新しい20個で宣言された場所をメ モリ上に確保します。
棚で例えると、全く別の棚(部屋数が20個)で管理する事と同じです。
よって、それより前に管理していた棚にはアクセスできません。

 棚の部屋にアクセスすには、部屋を特定できる番号が必要です。
棚名と部屋番号を特定できれば一意に識別出来る為、配列を同じようにします。
例えば、
array[0] = 10;

 では、部屋の最初にアクセスしています。
Javaでは、0からアクセスする為、10個の部屋がある場合は、0〜9が識別番号です。
「array[0]」の[]の中の事を添字と呼びます。
添字を指定する事によりその部屋をアクセス出来るようになります。
注意しなければいけないのが、部屋を特定する番号は0からスタートしないといけない事です。

 では、なぜ0がスタートなのでしょうか。
今まで説明していませんが、この管理する棚はすべて部屋が繋がっている為です。
よって、各棚は仮想的に区別されているに過ぎません。
仮想的とは1つの大きい棚が存在しているとします(これがメモリ全部だと思って下さい)。
物理的には1つな為、地域別に区切っていかないと(プログラム上では変数別)どこがどこの地域かが分からな くなってしまいます。
そこで、部屋の番号を識別する為に1から100までの部屋がこの地域で、101から200までがこの地域というように管 理されています。
そこで、「int[] array;」のプログラムは、この部屋の最初を確保する事に当たります。
この時点では、部屋の数が未定の為、部屋にアクセスしようとしてもコンパイルエラーになるわけです。
配列の変数の宣言は、部屋の最初の番号を確保する訳ですからその番号も当然している事になります。
その番号に相対的に(その地域の最初の番号を中心に)アクセスすれば、部屋を1つ1つ識別しアクセスできま す。

 例えば、東京都渋谷区に割り与えている最初の番号が5179の場合、最初の部屋は5179です。
なぜなら、地域ごとに割り当てなれている番号は、部屋の最初の番号で区別されているからです。
プログラムで「System.out.println(sibuya[0]);」とすれば、5179 + 0と解釈され渋谷区の最初の部屋にアクセ スする事になります。

4−2−4.サンプルプログラム

以下のソースを見てみましょう。
Sample4_2_1.java
public class Sample4_2_1 {

    public static void main(String[] args) {

        new Array().view();
    }
}

class Array {

    public void view() {

        int[] array = { 10, 20, 30, 40, 50 };

        // arrayの合計を求める
        int sum = 0;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "\t");
            sum += array[i];
        }
        System.out.println(array.length + "個の合計は" + sum);

        array[2] = -10;                                             // 3番目に-10を代入
        // arrayの合計を求める
        sum = 0;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "\t");
            sum += array[i];
        }
        System.out.println(array.length + "個の合計は" + sum);

        System.out.println();

        String[] strs = new String[30];                             // String[](文字列の配列)を宣言
        System.out.println("strsの部屋数は、" + strs.length);       // 配列の長さを表示
    }
}
10  20  30  40  50  5個の合計は150
10  20  -10 40  50  5個の合計は110

strsの部屋数は、30

 ソース上で説明されていない「array.length」や「strs.length」は配列の長さ(棚の部屋数)を表し、整 数です。
整数の為、計算に使う事もできますが代入をするとコンパイルエラーになります。
for文ではiが0からスタートして配列の長さになるまで繰り返しiを添字に使えば順々に処理をすることが可能 です。
大分プログラムらしくなってきました。
後は、自分でいろいろと改造して試してみるとfor文や配列の勉強になります。

2−5.配列の変数

 配列を理解するうえで大事なのが配列の変数です。
例えば、次の場合はどのような処理をするでしょうか。

int[] array = { 10, 20, 30, 40, 50 };
int[] copy = array;
copy[0] = 33;

 しつこいですがarrayは棚の最初の部屋番号なのでcopyには、arrayと同じ部屋番号が代入されます。
よって、「System.out.println(array[0]);」は33と表示されます。
ここに注意しましょう。
コピーするには、「int[] copy = (int[])array.clone();」とすれば全部要素がcopyにコピーされcopyの要素 を変更してもarrayには反映されません。

 Javaは、上記以外にも推奨されない宣言や初期化ができます。

4−2−6.多次元配列

 多次元配列は、配列の配列の配列…(2次元配列の場合は配列の配列)です。
Javaでは、プログラマは意識する必要がありませんが、内部では少し違います。
今回も例の地域管理で説明します。
最初の配列が1次元目(「配列変数[添字]」)、次の配列が2次元目(「配列変数[添字][添字]」)と続きま す。
2次元配列の場合は、最初の1次元目が2次元目を管理します。
配列の場合は、配列変数が配列の要素を管理していましたが、2次元配列の場合は、配列変数が1次元目の部屋 番号を管理し、1次元目が2次元目の部屋番号を管理します。
以下のソースを見てみましょう。
Sample4_2_2.java
public class Sample4_2_2 {

    public static void main(String[] args) {

        new DimensionArray().view();
    }
}

class DimensionArray {

    public void view() {

        // 2次元配列
        int[][] dimesion1 = { { 10, 20, 30 }, { 40, 50 }, { 60 } };

        int oneSum = 0;                                         // 小合計
        int sum = 0;                                            // 総合計
        for (int i = 0; i < dimesion1.length; i++) {
            for (int j = 0; j < dimesion1[i].length; j++) {
                oneSum += dimesion1[i][j];
                System.out.print(dimesion1[i][j] + "\t");
            }
            System.out.println("小合計 = " + oneSum);
            sum += oneSum;
            oneSum = 0;
        }
        System.out.println("\n総合計 = " + sum);
        
        System.out.println();
        System.out.println();

        // 個別に宣言
        int[][] dimesion2 = new int[3][];
        dimesion2[0] = new int[] { 10, 20, 30 };
        dimesion2[1] = new int[2];
        dimesion2[2] = new int[] { 60 };

        dimesion2[1][0] = 40;
        dimesion2[1][1] = 50;

        oneSum = 0;
        sum = 0;
        for (int i = 0; i < dimesion1.length; i++) {
            for (int j = 0; j < dimesion1[i].length; j++) {
                oneSum += dimesion1[i][j];
                System.out.print(dimesion1[i][j] + "\t");
            }
            System.out.println("小合計 = " + oneSum);
            sum += oneSum;
            oneSum = 0;
        }
        System.out.println("\n総合計 = " + sum);
    }
}
10  20  30  小合計 = 60
40  50  小合計 = 90
60  小合計 = 60

総合計 = 210


10  20  30  小合計 = 60
40  50  小合計 = 90
60  小合計 = 60

総合計 = 210

 配列の宣言と同時に要素の初期化や配列の宣言と2次元目の宣言と要素を個別に代入している方法がありま す。

 2次元目は、1次元目によって管理されている為2次元目移行は、個別に部屋を設定できるのです。
今回のサンプルは、2次元でしたが多次元配列は全て同じです。
注意するとすれば、3次元で配列の宣言及び初期化をする場合は 「int[][][] array = new int[添字][添字][];」のように、2つ添字を書かないとコンパイルエラーです。
N次元ならN-1個添字を書かないといけない又は一番右側書く必要が無いと覚えても良いでしょう。

4−2−7.推奨されない配列宣言

 配列には、推奨されない配列宣言があります。
これは、C言語からの影響で可能にしているに過ぎません(今までの方法で十分)。
●1次元配列
int array[] = new int[];
 この方法は、変数の方に[]が付いている事です。
型は、int[]型である為[]は型側に付いていないとおかしいのです。
●2次元配列(多次元配列)
int array[][] = new int[10][];
int[] array[] = new int[10][];
最初は、1次元配列の説明と同じ意味です。
最後は、1つだけ変数側にありこの宣言方法もJavaでは推奨されていません。

2−8.まとめ

 最後にまとめとして、配列のフォーマットを表示します。
●配列の宣言
・型[] 変数;

●配列の宣言と同時に初期化
・型[] 変数 = new 型[添字];

●配列の宣言と同時に配列要素を初期化
・型[] 変数 = new 型[]{ 初期化値, … };
・型[] 変数 = { 初期化値, … };


●2次元配列の宣言
・型[][] 変数;

●2次元配列の宣言と同時に初期化
・型[][] 変数 = new 型[添字][];

●2次元配列の宣言と同時に配列要素を初期化
・型[][] 変数 = new 型[][]{ { 初期化値 }, … };
・型[][] 変数 = { { 初期化値 }, … };


●アクセス方法
・型[添字];

●配列の長さ
・型.length;

1時間目 for文本に戻る     3時間目 while文及びdo〜while文に進む

単位4 処理を繰り返すにはに戻る

トップページに戻る