配列


月を数字で入力して, 対応する英単語を答えるプログラムを書いてみよう.
import java.io.*;
class Test8{
  public static void main(String[] args) throws IOException{
  // 入力をするためには,System.inからBufferedReaderを作らなくてはいけない
    BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
    System.out.print("月(1-12)を入力して下さい  ");
    // 入力をおこなう前に, バッファリングされていた出力を出してします
    System.out.flush();
    String s=d.readLine();
     // 文字列をint型に変換する. 
    int month=Integer.parseInt(s);
    // 入力した月が1より小さい時,12より大きい時は次の if 文を実行します
    if(month<1 || month>12) System.out.println(month+"月はありません");
    if(month==1) System.out.println("1月は January です. ");
    if(month==2) System.out.println("2月は Febrary です. ");
    if(month==3) System.out.println("3月は March です. ");
    if(month==4) System.out.println("4月は April です. ");
    if(month==5) System.out.println("5月は May です. ");
    if(month==6) System.out.println("6月は June です. ");
    if(month==7) System.out.println("7月は July です. ");
    if(month==8) System.out.println("8月は August です. ");
    if(month==9) System.out.println("9月は Sepember です. ");
    if(month==10) System.out.println("10月は October です. ");
    if(month==11) System.out.println("11月は November です. ");
    if(month==12) System.out.println("12月は December です. ");
  }
}
ここで,
    if(month<1 || month>12) System.out.println(month+"月はありません");
の「||」は複数の条件式の論理和をあらわす.(A||B)は「AまたはB」という意 味で,すなわち条件Aか条件Bのどちらかが真になると真になる.この条件式は 「monthの値が1より小さいとき,またはmonthの値が12より大きいとき」とい う意味である.

この逆が「&&」で論理積となる.(A&&B)は「AかつB」という意味で,条 件Aか条件Bの両方が真のときのみ真になる.

これで動くことは動くがあまりすっきりしないプログラムである. これを,

ように書き直してみよう.
import java.io.*;
class Test9{
  public static void main(String[] args) throws IOException{
  // 入力をするためには,System.inからBufferedReaderを作らなくてはいけない
    BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
    System.out.print("月(1-12)を入力して下さい  ");
    // 入力をおこなう前に, バッファリングされていた出力を出してします
    System.out.flush();
    String s=d.readLine();
     // 文字列をint型に変換する. 
    int month=Integer.parseInt(s);
    if(month<1 || month>12) System.out.println(month+"月はありません");
    else {
     String mname="";
     if(month==1) mname="January";
     else if(month==2) mname="Febrary";
     else if(month==3) mname="March";
     else if(month==4) mname="April";
     else if(month==5) mname="May";
     else if(month==6) mname="June";
     else if(month==7) mname="July";
     else if(month==8) mname="August";
     else if(month==9) mname="September";
     else if(month==10) mname="October";
     else if(month==11) mname="November";
     else if(month==12) mname="December";
     System.out.println(month+"月は "+mname+" です. ");
   }
  }
}
これで, すっきりとしたことはすっきりとしたが, else ifの繰り返しはまだ まだうっとおしい. 前のページのプログラムの
     else if(month==5) mname="May";
     else if(month==6) mname="June";
のような, 整数の値によって, 変数の値が変わるといったパターンを簡潔にプ ログラムするために, 多くのプログラム言語では配列が用意されている.
配列 を使ってプログラムを書き直すと次のようになる.
import java.io.*;
class Test10{
  public static void main(String[] args) throws IOException{
  // 入力をするためには,System.inからBufferedReaderを作らなくてはいけない
    BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
    System.out.print("月(1-12)を入力して下さい  ");
    // 入力をおこなう前に, バッファリングされていた出力を出してします
    System.out.flush();
    String s=d.readLine();
     // 文字列をint型に変換する. 
    int month=Integer.parseInt(s);
    // 文字列の配列型の変数 mnames を宣言し,{}で囲まれた初期値を入れる.
    String mnames[]={"January","Febrary","March","April","May","June",
                     "July","August","September","October",
                     "November","December"};
    if(1<=month && month<=12)
      System.out.println(month+"月は "+mnames[month-1]+" です. ");
    else 
      System.out.println(month+"月はありません");
  }
}

上のように
String mnames[]
と書くと文字列の配列を入れるmnames という名前の変数を宣言したことにな る. '='の右側の内容で配列の内容を初期化している. 配列の要素数(大きさ)は この例では12である.

配列型の変数の参照は 「変数名[添字]」のようにしておこなう. 添字は1で はなく0から始まることに注意しよう. 添字はint型でなくてはいけない.

float型,double型の変数を添字に使うのはキャスト演算子「(型名)式」でint 型に変換すれば可能だが,そうする必要がある場合の多くはその変数は本来 int型にすべきである.特に,ループの繰り返しを判定する変数などを, float, doubleにすると誤差により,ループの回数が意図しない回数になる可 能性がある.この場合は大(double)は小(int)を兼ねない.

注意)

    String mnames[]={"January","Febrary","March","April","May","June",
                     "July","August","September","October",
                     "November","December"};
    String mnames[];
    mnames={"January","Febrary","March","April","May","June",
            "July","August","September","October",
            "November","December"};
のように, 宣言と代入に分けて書くことはできない.

配列と繰り返し

配列の利用法としては, 前のページの例のように読み出し専用の表として利 用するだけでなく, データ領域として利用することもできる. 有名な「エラ トステネスのふるい」のプログラムをJavaで書いたものを下にあげる.
class Test11{
  public static void main(String[] args{
   int n=1000,i,k;
   int p[]=new int[n+1];
   for(i=2; i<=n; i++) p[i]=1; // i++はi=i+1と同じ意味
   for(i=2; i<=n; i++) 
     if(p[i]==1){
        System.out.print(i+" ");
        for(k=2; i*k<=n; k++) p[i*k]=0;
     }
  }
}
 このプログラムでは p という配列が使われている. iが素数の可能性がある
時はp[i]が1, 合成数の時はp[i]が0になるように計算していく. 配列の宣言と
作成は,
   int p[]=new int[n+1];
の部分である. これは,
   int p[];
   p=new int[n+1];
という2つの文に分けて書いても良い(続けて書かなくても良い). newは配列や オブジェクトの生成を表す予約語で, 次のintは要素の型を示す. [n+1]は作成 する配列の大きさ(要素数)が n+1 であることを示す. 配列の大きさを宣言時 に決定しなくてもよいのは, Javaの特徴の1つである.

上のプログラムで素数が求まる原理が分かりにくいかもしれない. はいぱーワークブック「プログラミング」でも 配列によるチェックのページでほとんど同じプログラムを扱っているので, 分からない人はそちらの説明を見て欲しい.

注)上のプログラムでは int の配列を使っているが, よりJavaらしく書くなら boolean(真偽値を表す型)の配列を使って

class Test12{
  public static void main(String[] args){
   int n=1000,i,k;
   boolean p[]=new boolean[n+1];
   for(i=2; i<p.length; i++) p[i]=true;
   for(i=2; i<p.length; i++) 
     if(p[i]){
        System.out.print(i+" ");
        for(k=2; i*k<=n; k++) p[i*k]=false;
     }
  }
}
のように書く. なお配列型の変数に .lengthをつけて p.lengthのようにする と, その配列の要素数を返す.
これまで, 呪文のように打ち込んできた
  public static void main(String[] args){
の「String[] args」の部分は, 実は文字列の配列である引数(「ひきすう」と 読む. 外から値を渡すためのものだが, 中から見ると初期化済の変数と思って 良い)だとわかる. 実は, java を起動する時に
java プログラム名 a1 a2 .. an
のように後ろに空白で区切って文字列をいくつか書くと
args[0] <- "a1"
args[1] <- "a2"
..
args[n-1] <- "an"
のように配列argsの中に渡すことができる. この機能を利用して, 次の プログラムを書いてみる.
class Test13{
  public static void main(String[] args){
    for(int i=args.length-1;i>0;i--)
      System.out.println(args[0]+args[i]);
  }
}
このプログラムをコンパイルして,
java Test13 I can read this file.
と実行すると,
ktanaka@ux019> java Test13 I can read this file.
Ifile.
Ithis
Iread
Ican
という出力が得られる.
配列が出てきたので,前回の課題の解答例(2)が理解できるはずなので説明する.
class Kadai1018{
  public static void main(String[] args){
      // 10001のサイズの配列を取ると 0-10000までの添字(index)が使える.
      // checkの配列は3乗の和でそれまで何通りの表わし方が見つかってい
      // るかを記録する
    int check[]=new int[10000+1];
      // 配列の要素をすべて0にする.Java言語では言語仕様で
      // intの配列の初期値は0と決まっているので,for文全体は不要
    for(int i=1;i<=10000;i++){
      // ^ のように,for文内だけで使う変数はその場で宣言することができる
      // この講義のサンプルではこのようにしないが,このような書き方を推奨
      // している本もある.
      check[i]=0;
    }
      // ペアの大きい方のiの範囲は1<=i<=10000(本当は9999で良い?)
    for(int i=1;(i*i*i)<=10000;i++){
      for(int j=1;j<=i;j++){
        int k=(i*i*i)+(j*j*j);
	  // 3乗和 k が10000以下なら,check[k]を1増やす.
        if(k<=10000){
          check[k]++;
        }
      }
    }
      // 1から10000までで,2回以上3乗和で表わされた回数が2以上のものを表示する.
    for(int i=1;i<=10000;i++){
      if (check[i]>=2){
        System.out.println(i);
      }
    }
  }
}

次に進む