Print Friendly, PDF & Email

この記事では、弊社の新人エンジニア研修の参考にJava8を解説します。

前回は繰り返し処理について解説しました。

コンピュータのすごさがあたらめて感じられたのではないでしょうか?

今回は配列の作成と使用について解説します。

これまで学んだとおり、プログラムでは基本的に変数に値を入れて処理をしていくのでした。

では、変数が10個、20個、100個と増えていったらどうなるでしょうか?

名前を考えるだけでも大変ですね。

そんな時に便利なのが配列です。

配列のイメージは同じ型の複数の値を入れる入れ物(変数)が並んだものです。

配列のイメージ

添字(index)を使って要素を指定します。

1.配列の使い方

1.配列を表す変数を宣言します

int[] ages;

データ型の後の[](角カッコ)が単なる変数ではなく配列であることを表しています。

2.配列の要素を確保します。[]の中の数値が要素数を表します。

ages = new int[3]

new演算子というものが使われています。

new演算子はJavaでインスタンス化(実体化)を行う際に使われるものです。

インスタンスについては後述します。

3.添字を用いて要素を指定し、配列に値を入れていきます。

ages[0] = 10;
ages[1] = 20;
ages[2] = 51;

このとき添字は0から始まるので、0,1,2と添字は2までしかないことに注意しましょう。

配列の添字(index)は0~(要素数-1)

4.配列に入っている値を添字を用いて参照します。

System.out.println(ages[2]);

51が表示されます。

サンプルコードにしてみました。

package chap06;

public class Example01 {

    public static void main(String[] args) {
        int[] ages;
        ages = new int[3];

        ages[0] = 10;
        ages[1] = 20;
        ages[2] = 51;

        for (int i = 0; i < 3; i++) {
            System.out.println(ages[i]);
        }
    }
}

<結果を表示>

10
20
51

この例では、配列の要素を繰り返しによって表示させています。このように、配列とfor文は相性が良いです。

また、配列の大きさ(要素の数)は次のようにして確認できます。

変数名.length

変数名.lengthという表現を使えば、上記のfor文は以下のようにも書けます。

for (int i = 0; i < ages.length; i++) {
	System.out.println(ages[i]);
}

マジックナンバーが消えて意味が分かりやすく、また配列要素数の変更にも強いプログラムになりました。

さらに、以下のように配列を一度に初期化する方法もあります。

package chap06;

public class Example02 {

    public static void main(String[] args) {
        int[] ages = {10, 20, 51};

        for (int i = 0; i < ages.length; i++) {
            System.out.println(ages[i]);
        }
    }
}

<結果>は先と同じ。

この場合には、3という要素数は自動で決まります。

2.ArrayIndexOutOfBoundsException

以下のように配列の要素を3つ確保すると

ages = new int[3];

添字は0から始まるので、0,1,2の3つの要素が用意されるのでした。

では、以下のように存在しない要素を指定すると何が起こるでしょうか?

package chap06;

public class Example03 {

    public static void main(String[] args) {
        int ages[];
        ages = new int[3];
        ages[3] = 10;
    }
}

以下のようなメッセージが出てプログラムの実行が中断されました。

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at chap06.Example03.main(Example03.java:8)
C:\Users\owner\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
ビルド失敗(合計時間: 0秒)

このように存在しない要素にアクセスしようとした場合は、例外が発生します。

今回発生した例外は、ArrayIndexOutOfBoundsExceptionという名前です。

このとても長い名前を持つ例外は、

Array「配列の」Index「添字が」OutOfBounds「範囲を超えた」Exception「例外」

という意味です。

これらのエラーメッセージにも意味がありますので、知らないものに遭遇したときには必ず、グーグルなどで調べるようにしてください。

目を背けてはいけません。

このとき「前もってIDEが例外の発生を教えてくれたらいいのに」と思ったかもしれません。

しかし、詳しくは例外のところで後述しますが、この例外はコンパイラがチェックしてくれない例外なのです。

コンパイラがチェックしてくれないので非チェック例外といいます。

プログラマの責任で対処すべき例外なのです。

3.参照とハッシュ値

ところで、上記の例では、ages[0]には10が、ages[1]には20が、ages[2]には51が入っていました。

では、agesという変数には何が入っているのでしょうか?

System.out.println(ages);

としてみると、

[I@15db9742

上記の文字列が表示されました。

この文字列の@以降はハッシュコードと呼ばれるものです

データの置き場所であるメモリのアドレスだと思っていただければ大きく違ってはいません。

ちなみにこのハッシュはごちゃまぜにするという意味でハッシュドビーフ(hashed beef)などと同じ語源です。

なお、先頭の “[ ”は配列であることを意味しています。

ここまでのところを図解すると以下のようになります。

①JVMが管理するメモリ領域には大きく分けてスタック領域とヒープ領域の2つがあります。

スタック領域はローカル変数やメソッドが格納される領域で、ヒープ領域はインスタンスが格納される領域です。

メモリ内の動作のイメージ①
メモリ内の動作のイメージ①

②メモリにはアドレスがあります。

ローカル変数に格納されるのは、配列が格納される予定のヒープ領域の先頭アドレス(ここでは15db9742)です。

メモリ内の動作のイメージ②
メモリ内の動作のイメージ②

③要素がヒープ領域に格納されます。

メモリ内の動作のイメージ③
メモリ内の動作のイメージ③

このような仕組みのため、

System.out.println(ages);

とすると以下の表示になるわけです。

[I@15db9742

なお、Javaの参照型は以下の3つに整理されます。

配列と、クラス(インスタンス)インターフェースです。

例えば、次回学ぶ文字列はクラス型に分類できます。

4.配列の要素を一度に表示する

標準出力を使って配列の要素を一度に表示するにはどうしたらよいでしょうか?

毎回、繰り返し処理を使って表示させるのもおっくうです。

そんな時に便利なのが配列の要素を文字列化してくれるArraysクラスのtoStringメソッドです。

Arraysは「配列」という意味の英語です。

Arrays.toStringの意味はArrays「配列を」toString「文字列へ」ということで、名が体を表すネーミングですね。

package chap06;

import java.util.Arrays;

public class Example04 {

    public static void main(String[] args) {
        int[] ages = {10, 20, 51};
        System.out.println(Arrays.toString(ages));
    }
}

※パッケージ宣言とクラス宣言の間に「import java.util.Arrays;」という一文を挿入する必要があります。IDEを使うと簡単に挿入できますので講師に教わってください。 また、importの意味は、2回目の記事でお話ししました。

<結果>

[10, 20, 51]

デバッグ時に便利ですので覚えておいてください。

5.拡張for文

配列と相性が良いのが拡張for文です。

拡張for文は、配列の先頭から最後までを連続して処理するときに簡潔に書ける構文です。

まずはイメージです。

配列の要素を0番目から一つずつ一時的な変数に格納して処理します。

一時的な変数ageに格納している拡張for文のイメージ
一時変数ageに配列の要素を格納している拡張for文のイメージ

<構文>

(一時変数の宣言 : 配列の変数)

初学者の方はforの()の中の変数宣言と配列の変数指定の順番を逆にしないように気をつけてください。

また、区切りは;(セミコロン)ではなく(:コロン)ですので間違えないようにしましょう。

もっとも便利なIDEのコードテンプレートを使えば間違いないですが。

なお、この(:コロン)は「中の」といった程度の意味です。

サンプルコードで確認してみましょう。

package chap06;

public class Example05 {

    public static void main(String[] args) {
        int[] ages ={10,20,51};

        for (int age : ages) {
            System.out.println(age);
        }
    }
}

<結果を表示>

10
20
51

このようにほんの少しですがシンプルに書くことができました。

この拡張for文はのちにコレクション(ArrayList)というものを学んだ時にも頻出しますので、今のうちに慣れておいてください。

繰り返し処理をするために一時的に“使い捨て”の変数ageを使っています。

この変数の値を繰り返し処理の中で変更しても元の配列には影響しませんので注意してください。

以下はそのことを示すサンプルコードです。

配列の要素をそれぞれ2倍にしたつもりですが、できていません。

package chap06;

public class Example06 {

    public static void main(String[] args) {
        int[] ages = {10, 20, 51};
        for (int age : ages) {
            System.out.println(age);
            age *= 2;
        }
        for (int age : ages) {
            System.out.println(age);
        }
    }
}

<結果を表示>

10
20
51
10
20
51

変数ageがローカル変数である以上、当然といえば当然ですが間違えやすいところです。

通常のfor文と比較した場合の拡張for文のメリットは、i,jなどの変数が不要になり、バグを作り込む可能性が低下することです。

一方、通常のfor文と比較した場合の拡張for文の制限は、必ず配列の先頭からの処理となることです。

配列の後ろから処理をしたい場合は、直接対応できません。

あらかじめ逆順にソートしておく前処理が必要になります。

さらに、要素の一つ飛ばしに処理を加えたい場合などにはあまり向かないでしょう。

したがって通常のfor文が不要になるわけではありませんが、拡張for文が使える場面では拡張for文を優先させて下さい。

6.2次元配列

ここまで作成してきた配列は1列でしたので1次元配列と呼ばれます。

それに対して、2次元以上の配列を多次元配列といいます。

ここでは、2次元配列を見ていきましょう。

イメジとしてはエクセルの表に少し似ています。

ただし、以下の図のように本当は1次元の配列を複数用意したものです。

2次元配列の本当の姿

サンプルコードを見てみます。

package chap06;

public class Example07 {

    public static void main(String[] args) {
        int[][] scores = {
            {100, 80, 90, 90},
            {90, 90, 90, 90},
            {60, 90, 80, 70},};
        System.out.println(scores[0][3]);
        System.out.println(scores[2][3]);

        for (int[] score : scores) {
            for (int i : score) {
                System.out.print(i + ",");
            }
        }
    }
}




<結果を表示>

40
30
10,20,30,40,50,60,70,80,90,10,20,30,

このようにシンプルに書けるのが拡張for文の良いところです。

なお、このサンプルでは拡張for文を入れ子にしていましたね。

2次元以上の配列について、研修では深入りしませんので、このサンプルコードを示すだけとします。

7.配列とオブジェクト指向

なお、配列の利用場面はかつてほどではありません。

なぜなら、配列は要素数を後から変更できません。

また、配列はオブジェクトではないため、メソッドを持たせることもできません。

これら配列の弱点を克服したArrayListについては後で学びましょう。

※例えば、Androidの正式開発言語となったKotlinはJavaの後継言語とも目されていますが、配列は存在せず、常にArrayクラスを使います。

昔は(といっても1990年代です)、オブジェクトという考え方がなく配列を使った処理を多用していました。

例えば、以下は5名の生徒の成績を管理するプログラムです。

package chap06;

public class Example08 {

    public static void main(String[] args) {
        int score[] = {80, 75, 100, 90, 80};
        String label[] = {"Tom", "John", "Mary", "Ken", "Jimmy"};
        for (int i = 0; i < score.length; i++) {
            System.out.println(label[i] + "'s score is " + score[i]);
        }
    }
}

<結果を表示>

Tom's score is 80
John's score is 75
Mary's score is 100
Ken's score is 90
Jimmy's score is 80

しかし、現代では1人分のデータは1人分のオブジェクト(より正確にはインスタンス)として表現することが一般的です。

何でも配列に入れていた時代もあった
何でも配列に入れていた時代もあった

この点は何度も繰り返しお伝えしていくテーマなので、今は用語に慣れる程度で結構です。

今回はJavaの配列の作成と使用について見てきました。

次回は、文字と文字列の扱いを学んで、文字列も実は参照型であるということについて学びましょう。

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□配列は同じ型の複数の変数が並んだものと考えられる

□配列の添字(index)は0~(要素数-1)である

□拡張for文は、配列の先頭から最後までを連続して処理するときに簡潔に書ける構文である

□参照にはハッシュコードが入っている

まとめができたら、アウトプットとして演習問題にチャレンジしましょう。

問題6.配列

変数が100個あったとしたら名前を考えるだけでもひと仕事です。 しかし、同じ型の変数であれば、配列として扱うことができます。 さらに前回学んだ繰り返しと配列はとても…

【今回の復習Youtube】
 

013-Javaの基本-配列の初期化

014-Javaの基本-配列とfor文

015-Javaの基本-配列のネスト

016-Javaの基本-配列のネストとfor文

JavaSE8の解説に戻る