入門じゃないJava(7)

こんにちは。

今日もJavaを楽しくー見ていきましょう。

 

前回は配列を見ていきましたが、今日も配列を見ていきたいと思います。

creators.hateblo.jp

できれば、今日で一次元配列を終わらせて、早く多次元配列に入りましょう。

棒グラフの表示

import java.util.Random;
import java.util.Scanner;

class IntArrayRand {
    public static void main(String[] args) {
        Random rand = new Random();
        Scanner stdIn = new Scanner(System.in);
        
        System.out.print("配列要素数>");
        int n = stdIn.nextInt();
        int[] a = new int[n];

        for(int i = 0; i < n; i++)
            a[i] = 1 + rand.nextInt(10);
        
        for(int i = 0; i < n; i++){
            System.out.printf("a[%2d] : ", i);
                for(int j = 0; j < a[i]; j++)
                    System.out.print("*");
                System.out.println();
            
        }
    }
}
×

実行結果(一例)

配列要素数>10
a[ 0] : *
a[ 1] : ***
a[ 2] : ******
a[ 3] : **********
a[ 4] : *******
a[ 5] : *******
a[ 6] : ***
a[ 7] : ******
a[ 8] : ****
a[ 9] : ********

C:\Java\07>_

 上のプログラムを眺めながら前回までの流れを思い出してみましょう。

 

 配列の初期化と代入

 配列の全要素が既定値0で初期化されるとはいえ、個々の要素に入れるべき値があらかじめ分かっているのであれば、明示的に初期化を行うべきです。

 

class IntArrayInit {
public static void main(String[] args) {
    int[] a = {1, 2, 3, 4, 5, 6, 7, 8};
    
    for(int i = 0; i < a.length; i++)
        System.out.println("a[" + i + "] : " + a[i]);
    }
}
×

実行結果

a[0] : 1
a[1] : 2
a[2] : 3
a[3] : 4
a[4] : 5
a[5] : 6
a[6] : 7
a[7] : 8

C:\Java\07>_

 

 配列に与える初期化子は、各要素に対する初期化子をコンマ, で区切って順に並べてそれを{ }で囲ったものです。生成される配列の要素数は初期化子の個数に基づいて自動的に決定されます。

本プログラムでは明示的にnewしていないにもかかわらず要素数8の配列が生成されます。

なお、以下は×

int a;

a = {1, 2, 3, 4, 5, 6, 7, 8};

 正しくは下記の通り

int a;

a = new int{1, 2, 3, 4, 5, 6, 7, 8};

というのも、new演算子による配列生成時は"new 要素型"の後に初期化子をつけてもよいことになっているからです。

もう一度、正しくは、

要素型 変数名;

変数名 = new 要素型{要素, 要素, 要素}; 

 という形式です。

 

配列要素への値の読込み

import java.util.Scanner;
class ComingAggregate {
    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);

        final int BIZ_HOURS = 8;              //時間
        int sum = 0;                          //合計
        int[] visitors = new int[BIZ_HOURS];  //来客数
        
        System.out.println("時間ごとの来客数を入力してください(9時~17時)");
        for(int i = 0; i < BIZ_HOURS; i++) {
            System.out.printf("%2d時~%2d時までの来客数>", i + 9, i + 10);
            visitors[i] = stdIn.nextInt();
            sum += visitors[i];
        }
        
        System.out.printf("総来客数は%d人です。\n", sum);
        System.out.printf("平均来客数は%.2f人です。\n", (double)sum / BIZ_HOURS);
    }
}
×

実行結果

C:\Java\07>java ComingAggregate
時間ごとの来客数を入力してください(AM10~PM6)
 9時~10時までの来客数>33
10時~11時までの来客数>25
11時~12時までの来客数>12
12時~13時までの来客数>11
13時~14時までの来客数>15
14時~15時までの来客数>17
15時~16時までの来客数>26
16時~17時までの来客数>17
総来客数は156人です。
平均来客数は19.50人です。

C:\Java\07>_

 

 配列の要素の最大値を求める

import java.util.Scanner;
class ComingMaxTime {
    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);

        final int BIZ_HOURS = 8;              //時間
        int sum = 0;                          //合計
        int max = 0;                          //最大値
        int[] visitors = new int[BIZ_HOURS];  //来客数
        
        System.out.println("時間ごとの来客数を入力してください(AM10~PM6)");
        for(int i = 0; i < BIZ_HOURS; i++) {
            System.out.printf("%2d時~%2d時までの来客数>", i + 9, i + 10);
            visitors[i] = stdIn.nextInt();
            sum += visitors[i];
            if(visitors[i] > max) max = visitors[i];
        }

        System.out.printf("総来客数は%d人です。\n", sum);
        System.out.printf("平均来客数は%.2f人です。\n", (double)sum / BIZ_HOURS);
        System.out.printf("最大来客数は%d人です。\n", max);
    }
}
×

実行結果

C:\Java\01>java ComingAggregate
時間ごとの来客数を入力してください(AM10~PM6)
9時~10時までの来客数>33
10時~11時までの来客数>25
11時~12時までの来客数>12
12時~13時までの来客数>11
13時~14時までの来客数>15
14時~15時までの来客数>17
15時~16時までの来客数>26
16時~17時までの来客数>17
総来客数は156人です。
平均来客数は19.50人です。
最大来客数は26人です。

C:\Java\07>_

先ほどのコードをベースにしています。

また、肝心な処理が少々腫れぼったい感じもしますが、ふるまい的には大きくは変わりません。

 

線形探索

探索は配列の要素の先頭から順に走査することによって実現てきます。探すべきキー値と同じ値の要素に出会ったら探索に成功です。こては線形探索、または逐次探索と呼ばれるアルゴリズムです。

 ここで、解説するよりも先にコードを見てみましょう。

 

import java.util.Random;
import java.util.Scanner;

class LinearSearch {
    public static void main(String[] args){
        Random rand = new Random();
        Scanner stdIn = new Scanner(System.in);

        final int n = 12;
        int[] a = new int[n];

        for (int j = 0; j < n; j++)
                a[j] = 1 + rand.nextInt(10);
        
        System.out.print("配列内の全要素の値\n{ ");
        for (int j = 0; j < n; j++)
                System.out.print(a[j] + " ");
        System.out.println("}");

        System.out.print("探す数値>");
        int key = stdIn.nextInt();
        
        int i;
        for (i = 0; i < n; i++)
                 if (a[i] == key)
                         break;

        if (i < n)
                System.out.printf("%dはa[%d]にあります。", key, i);
        else
                System.out.printf("%dはありません。", key);
    }
}
×

実行結果(一例:探索成功)

C:\Java\07>java LinearSearch
配列内の全要素の値
{ 5 10 7 6 10 1 9 10 10 10 8 9 }
探す数値>8
8はa[10]にあります。

C:\Java\07>_

×

実行結果(一例:探索失敗)

C:\Java\07>java LinearSearch
配列内の全要素の値
{ 4 3 5 6 1 5 1 2 9 1 9 1 }

探す数値>8
8はありません。

C:\Java\07>_

 

 探索の失敗と成功はiとnの比較で判定しています。

では、そもそも、成功と失敗の判断基準は何でしょうか?

探索すべきキー値が見つからず、末端を通り過ぎた(及び通り越しそうになった)。

探索すべきキー値と等しい要素を見つけた。

 ①が成立した時は探索失敗、②が成立したときは探索成功を意味します。

すると、要素数nの配列aからkeyと同じ値を持つ要素を探索するプログラムは、

int i;

for (i  = 0; i < n && a[i] != key; i++){

    ;

// i < nであれば探索成功、そうでなければ探索失敗

 先ほどの通り①か②のどちらか一方でも成立した時、繰り返しは終了します。

変数iはfor文を抜けた後に判定を行うため、for文の中ではなく前に宣言します。

もしも、、、

iがnと等しい

探索失敗→すべての走査を終了しているため、探索失敗。

iがnより小さい

探索成功→走査中にa[i]とkeyの値が一致したため②が成立しforを抜けているため探索成功。

 

なお、これらはbreak文を利用して書き換えることができます。

int i;

for (i = 0; i < n; i++)

    if (a[i] == key)

        break;

// i < nであれば探索成功、そうでなければ探索失敗

 もう一度上のプログラムを見てみるとあっ!

とひらめく個所もあるでしょう。

 

今日はこの辺で失礼します。

では!

 

参考文献

柴田 望洋 (2016) .『新・明解Java入門』186-195