|
通常、どのようなプログラム言語においても、配列とは大体以下のように定義されます。
配列内の要素は同じ型であり、0から始まるインデックス番号によりアクセスすることができる。
つまり、配列とは下図のような、0から始まる番号を持つ仕切りつきの箱のようなものです。

Javaでは配列は以下のように宣言します。

@普通のJavaの構文と同じように変数の型と名称を宣言します。つまりこの場合は「int型の配列」型でiaと言う変数を宣言するということです。そこから考えるとクラス型であるStringの配列も「String型の配列」型となるわけです。
→ 他言語と同様に「int ia[] 」と宣言することも可能ですが、上記の説明を理解するには「int[] ia」の方が簡単だと思います。
Aここは初心者の人には非常にわかりにくいかもしれませんね。「プリミティブなのにnew?」という疑問が湧いてくると思います。これはこのように理解してください。@で宣言した「int型の配列」型と言うのはクラス型と同じ扱いになるのです。重要な概念です!飽くまでもここではプリミティブなint型の値を3つ格納することが可能なオブジェクトをnewしていると考えてください。
→ これは以下のようなコードで簡単に理解できます。これはコンパイルエラーになりません。
|
例:「int型の配列」型の実体はObject型として扱える
|
|
|
以下に「int型の配列」型であるia1とia2を宣言するためのコード例を掲示します。
|
例:int型の配列宣言のコード例
|
| |
|
|
| |
int[] ia1 = new int[3];//int型の3つの配列の宣言
ia[0] = 1;
ia[1] = 2;
ia[2] = 3;
int[] ia2 = { 1 , 2 , 3 };//一括して宣言
|
|
| |
|
|
|
ia1の配列の時にはまず配列を準備し、各要素に値を代入しています。またia2の時にはia2の宣言と値の代入を同時に行っています。要素の初期値が決まっておらず、動的に値を代入していく場合は前者を、逆に初期値が決まっている場合は後者を利用するのが一般的です。いずれにしても結果は変わらないので場合によって使い分けてください。
では、もし配列の宣言のみ行った場合はどうなるのでしょうか?ちなみに宣言のみと言うのには2つの場合があると思います。
- 配列型の変数の宣言のみ → int[] ia; のような場合
- 配列型の変数の宣言と生成のみで配列要素に関しては未指定 → int[] ia = new ia[3]; のような場合
最初の場合はiaがただ単にnullとなるだけです。なぜなら「int型の配列」型であるiaにはnewされたオブジェクトが入っていないからです。では後者の場合はどうでしょうか?「int型の配列」型であるiaにはint型の値を3つ格納できるオブジェクトが代入されています。そのためiaはnullではありません。しかし要素に関しては何も代入していません。
ではia[0] , ia[1] , ia[2]には何が入っているのでしょうか?
答え、ia[0] , ia[1] , ia[2]にはそれぞれint型の0が入っています。配列の要素の初期値は下に示す表のように定義されています。
|
型
|
デフォルト値
|
| byte |
ゼロ,すなわち (byte)0 とする。 |
| short |
ゼロ,すなわち (short)0 とする。 |
| int |
ゼロ,すなわち 0 とする。 |
| long |
ゼロ,すなわち 0L とする。 |
| float |
正のゼロ,すなわち 0.0f とする。 |
| double |
正のゼロ,すなわち 0.0d とする。 |
| char |
空文字,すなわち '\u0000' とする。 |
| boolean |
false とする。 |
| 参照型(クラス型) |
null とする。 |
プリミティブな型に関しては、boolean型以外は基本的に0ですね。ここで問題となるのは表の一番下の行です。「クラス型の配列」型の要素の初期値はnullとなっています。つまり配列をnewしただけでは要素には何も入っていないということです。少しわかりにくいので下記のコードを見てください。
|
例:String型の配列の要素の初期値1
|
| |
|
|
| |
//「String型の配列」型のオブジェクトをnew
String[] strs = new String[3];
//配列の0番目のStringの文字列の長さを返すlength()メソッドを実行
System.out.println( strs[0].length() );
|
|
| |
|
|
|
このコードはコンパイルはできますが、実行するとstrs[0].length()の行でNullPointerExceptionが発生します。理由は簡単です。strsは配列の宣言のみなので各要素は初期値のままです。Stringはクラス型なので初期値はnullです。すなわちstrs[0]の値はnullのため実体が存在しません。実体がないのにlengthメソッドを実行(参照)しようとしたためにExceptionが発生します。これを防ぐためには下記のように何らかのStringのインスタンスを代入してやらなければなりません。
|
例:String型の配列の要素の初期値2
|
| |
|
|
| |
//このあと参照するための変数
String tmpStr = "実験3";
//「String型の配列」型のオブジェクトをnew
String[] strs = new String[3];
strs[0] = "実験1";
strs[1] = new String("実験2");//新たにnew
strs[2] = tmpStr; //他の変数のインスタンスを代入
//配列の宣言と要素の代入を同時に行う、やっていることは上記と同一
String[] strs2 = { "実験1" , new String("実験2")
, tmpStr };
//配列の0番目のStringの文字列の長さを返すlength()メソッドを実行
System.out.println( strs[0].length() );//strs[0]は"実験1"の値を持つStringインスタンスが存在する
|
|
| |
|
|
|
よろしいでしょうか?ここまでを簡単にまとめると
- 配列と配列の中身(要素)は別物である。
- 配列自体は「〜型の配列」 型のオブジェクトであり、通常のクラス型と同じように扱うことができる。
- プリミティブ型の要素は0(boolean型はfalse)で初期化されるが、クラス型の要素はnullで初期化される。
となります。この理解の仕方が今後のJavaの学習を左右すると言っても過言ではありません。もし理解しきれていなければ気分を落ち着けて上からもう一度読み直してみてください。
|
|
配列の利用法はいたって簡単です。インデックス番号を利用して要素にアクセスするだけです。例えば下のコードのように配列の中身を参照します。
|
例:配列の参照
|
| |
|
|
| |
//配列の宣言
String[] names = { "牧野", "小林" ,
"尾形" , "井出" , "せいたろう"
};
//for文で出力
for( int i=0 ;i<names.length;i++) {//namesはオブジェクトだからフィールドの参照ができる
System.out.println( names[i] );
}
|
|
| |
|
|
|
配列のフィールドであるlengthを参照してこの配列の要素数を取得し、0から1ずつ要素数-1(配列の番号は0から始まるため)までiをカウントしています。そしてそのiを配列を参照するときの要素番号として利用しています。ちなみに存在しない配列に対してアクセスするとArrayIndexOutOfBoundsExceptionがスローされ例外となります。(例えば3つの配列の4番目にアクセスするなどがある)
配列の利用法というのは基本的にはこれだけです。
ここからはほんの少し高度になりますが、もし難しければいったん飛ばしてもかまいません。このあと2次元配列などの多次元配列が出てきますが、私の個人的な見解からすればグラフィックスなどの座標計算でも行わない限りは多次元配列の必要性はありません。なぜなら多次元配列はアクセスが複雑であり取り扱いが直感的でないからです。例えば多次元配列が必要になる多くの場合は、下のような2次元の表構造だと思われます。
|
名前
|
性別
|
年齢
|
|
牧野
|
男
|
25
|
|
尾形
|
男
|
22
|
|
井出
|
女
|
23
|
この表構造を2次元のString配列として扱うと下記のようなコードになります。
|
例:2次元配列
|
| |
|
|
| |
//2次元配列の宣言
String[][] names = { {"牧野", "男"
, "25"} ,
{"尾形", "男" , "22"}
,
{"井出", "女" , "23"}
};
//尾形の名前、性別、年齢を表示するとき(2行目)
System.out.println( names[1][0] );
System.out.println( names[1][1] );
System.out.println( names[1][2] );
//for文で全件を表示
for( int a=0;a<names.length;a++ ){
for( int b=0;b<names[a].length;b++ ){
System.out.println( names[a][b] );
}
}
|
|
| |
|
|
|
この場合は表示だけなので比較的簡単でありますが、表が複雑になり
「58行目の4列目と6列目と158列目を取り出してデータベースに格納せよ。」
という命題などが与えられたときには簡単には対処できません。また仕様変更などもってのほかです。
このような時は情報のカプセル化を行ってそれを1次元配列として扱ってしまいましょう。具体的には下記のようなUserNameクラスを設計し、それを配列として宣言し利用してあげるのです。この方がよりシンプルに情報にアクセスでき、情報の取得もget〜のように直感的な名称が利用できます。また仕様変更のときもカプセル化されていることから、このクラスを利用している他のクラスにはほとんど影響がありません。詳細なカプセル化や隠蔽に関しては他の節での説明に譲ります。ここでは多次元配列は特定の状況でしか利用すべきではないと理解していただければよいかと思います。
|
例:ユーザー情報のカプセル化とその配列(ソースファイルのダウンロード)
|
| |
|
|
| |
/**
ユーザー情報を保持するクラス
*/
public class UserInfo extends Object{
/** 名前 */
private String name = null;
/** 性別 */
private String sex = null;
/** 年齢 */
private String age = null;
/**
コンストラクタ
@param String sname 名前
@param String ssex 性別
@param String sage 年齢
*/
public UserInfo( String sname , String ssex , String
sage ){
this.name = sname;
this.sex = ssex;
this.age = sage;
}
/**
名前をセット
@param String str 名前
*/
public void setName(String str){
this.name = str;
}
/**
名前を取得
@return String 名前
*/
public String getName(){
return this.name;
}
/**
性別をセット
@param String str 性別
*/
public void setSex(String str){
this.sex = str;
}
/**
性別を取得
@return String 性別
*/
public String getSex(){
return this.sex;
}
/**
年齢をセット
@param String str 年齢
*/
public void setAge(String str){
this.age = str;
}
/**
年齢を取得
@return String 年齢
*/
public String getAge(){
return this.age;
}
/**
二次元配列の替わりにカプセル化したオブジェクトの配列を用いるサンプル
@param String[] args 起動時パラメーター
*/
public static void main( String[] args ){
//ユーザー情報配列
UserInfo[] users = { new UserInfo("牧野","男","25")
,
new UserInfo("尾形","男","22")
,
new UserInfo("井出","女","23")
};
//尾形の名前,性別,年齢を表示
System.out.println( users[1].getName() );
System.out.println( users[1].getSex() );
System.out.println( users[1].getAge() );
//for文で全件を表示
for( int i=0;i<users.length;i++ ){
//名前,性別,年齢を表示
System.out.println( users[i].getName() );
System.out.println( users[i].getSex() );
System.out.println( users[i].getAge() );
}
}
}
|
|
| |
|
|
|
|