■配列■

配列とは

同じ型のデータをn個並べたものを,まとめて配列と呼ぶ.例えばたくさんの体重を扱うとする.次のように人数分のプログラムが必要になる.

int weight1; //1人めの体重
int weight2; //2人めの体重
...
int weightN; //N人めの体重

weight1を使った計算処理
weight2を使った計算処理
...
weightNを使った計算処理

このweight1,weight2などを,weight[1], weight[2]と置き換えて考えてみよ.このときweightを配列名,weight[1],weight[2]などを配列要素と呼ぶ.

配列を使えば,人数がどれだけ増えてもfor文で繰り返し処理が書けるため,必ず5行で書くことができる.

int [] weight = new int[N]; //N個を一度に宣言
for(int i=0; i<N ; i++)
{
   weight[i]を使った計算処理 //繰り返しで書ける!
}

宣言方法

宣言の例は次のとおり.必ずnewによって場所を確保する.newで確保された場所は,全要素が0値(0.0値)で初期化されていることが保証されている.(boolean型の配列の場合にはfalseで初期化.)

final int NUM = 100;
double[] array1 = new double[NUM]; //doubleの要素を100個分用意
 ↑       ↑           ↑    ↑
配列要素の型  配列要素の型 配列要素の数
         配列名 (さっきと同じ型)

配列要素の型はなんでもよい(int,double,booleanなど)が,配列の要素番号を指定する[]の中には,整数(int)しか書くことができない.

boolean[] array2 = new boolean[NUM];
                                ↑
                要素の型とは関係なく整数しか書けない

使い方

実行文中での参照は,次のとおり.この例では0から99までの100個が使える.

double x = array[10]; //11番目の要素を読み出す
array[14] = 128.487;  //15番めの要素に書き込む

1次元配列の初期化

配列を0値以外で初期化することもできる.

int[] array = "0, 1, 2, 3, 4, 5, 6, ......., 99";

この場合,配列の要素 array[i]には値iが格納された状態になる.しかし,初期化値の個数が配列の個数となるため,大きな個数を宣言するのは大変である.そのため,例えば数を順番に初期化する場合には,次のようにfor文で初期化する.

final int NUM = 100;
int[] array = new int[NUM]; //配列場所の確保

for(int i=0; i<NUM; i++)
{
  array[i]=i;
}

1次元配列の長さの取得

C言語での配列とは異なり,javaでは配列の長さを簡単に取得することができる.

int l = array.length;

1次元配列のコピー

intやdoubleなどの基本型の変数の場合,値をコピーするには単に代入すればよかった.

int i1=100;
int i2 = i1; //i1の中身をi2にコピー

しかし,配列は,複数のデータの集合体なので次のように書いてもコピーできない.

int[] array1 = {1, 2, 3, 4};
int[] array2 = array1; //array1をコピー!?(できてません)

array1[0]ではなく,array1だけと表記すると,配列全体の場所(メモリのアドレス)を意味する.このため,この場合array2へarray1のデータの場所を代入することになり,両方共まったく同一のデータ(メモリ領域)を参照することになる.つまりarray1[0]を書き換えるとarray2[0]も書き換わってしまう.

メモリ上のデータ{1,2,3,4}
              ↑  ↑
           array1 array2 ※同じメモリを参照

配列をコピーするには,全要素をひとつひとつコピーしてやる必要がある.

int[] array1 = {1, 2, 3, 4};
int[] array2 = new int[array1.length];
   //array1と同じサイズだけど,別の場所を新たに確保
for(int i; i<array1.length; i++) //配列の長さ
{
    array2[i] = array1[i];  //要素をひとつづつコピー
}

二次元配列

二次元の配列は次のように考える.例えば画像データは次のように表現できる.

final int X_NUM = 640;
final int Y_NUM = 480;
int [][] bitmap = new int[Y_NUM][X_NUM];

実際の二次元配列は,次のようにメモリ上に格納されている.

bitmap[0][0] bitmap[0][1] bitmap[0][2] ... bitmap[0][639]
bitmap[1][0] bitmap[1][1] bitmap[1][2] ... bitmap[1][639]
bitmap[2][0] bitmap[2][1] bitmap[2][2] ... bitmap[2][639]
... ... ... ... ...
bitmap[479][0] bitmap[479][1] bitmap[479][2] ... bitmap[479][639]

不揃いな二次元配列

1次元配列のarrayのn番めがarray[n]であったのと同じで,二次元配列bitmapの場合には,bitmap[m]のn番めがbitmap[m][n]となる.よく考えれば,bitmap[m]がintの配列を表していることになり,つまり2次元配列とは「配列の配列」である.

前の例はintの1次元配列(int[])を格納する配列の配列(bitmap[m])を,bitmap[0]からbitmap[479]までの480個一気に宣言したと考えられる.さらにこれをよく考えて応用すると,二次元配列で次のように不揃いな二次元配列も宣言できる.

int[][] array2d = new int[5][]; //1次元目だけ確保
array2d[0] = new int[2]; //2次元目の数は不揃いでもよい.
array2d[1] = new int[5];
array2d[2] = new int[3];
array2d[3] = new int[1];
array2d[4] = new int[4];
array2d[0][0] array2d[0][1]
array2d[1][0] array2d[1][1] array2d[1][2] array2d[1][3] array2d[1][4]
array2d[2][0] array2d[2][1] array2d[2][2]
array2d[3][0]
array2d[4][0] array2d[4][1] array2d[4][2] array2d[4][3]

このとき,配列の長さは次のようになる

array2d.length    → 5  配列の縦の長さ
array2d[0].length → 2  縦位置0の所での横の長さ
array2d[1].length → 5  縦位置1の所での横の長さ
array2d[2].length → 3  縦位置2の所での横の長さ
array2d[3].length → 1  縦位置3の所での横の長さ
array2d[4].length → 4  縦位置4の所での横の長さ

二次元配列の初期化

二次元配列の場合も初期値を使って宣言することができる.

int [][] bitmap = { 
                     {0,0,0,1,0,0},
                     {0,1,0,0,0,0},
                     {0,1,1,0,0,0},
                     {0,1,0,1,0,0},
                     {0,1,0,0,1,0},
                     {0,1,0,0,0,1}
                   };

二次元配列のコピー

2次元配列のコピーは,相手が不揃いな2次元配列かもしれないため,少し複雑になる.

int [][] bitmap1 = { 
                     {0,0,0,1,0,0},
                     {0,1,0,0,0,},
                     {0,1,1,0},
                     {0,1,0,1,0,0},
                     {0,1,0,0},
                     {0,1,0,0,0,1}
                   };
int [][] bitmap2 = new int[bitmap1.length][];
        //1次元目だけbitmap1と同じだけ確保

/* 2次元目を確保する部分 */
for(int i=0; i<bitmap1.length; i++) //配列の縦の長さ
{
  bitmap2[i] = new int[bitmap1[i].length]; /iの所での配列の横の長さ
  //bitmap1[i]と同じサイズのint[]を2次元目として確保
}

/* コピーする部分 */
for(int y=0; y<bitmap1.length; y++) //配列の縦の長さ
{
  for(int x=0; x<bitmap1[y].length; x++) //yの所での配列の横の長さ
  {
    bitmap2[y][x] = bitmap1[y][x];
  }
}

宣言と実行文での[]の意味の違い

初心者が混乱する点であるが,宣言のときの[]と実行文での[]は意味が異なる.特に,二次元配列(後で詳しく説明する)で,その違いがはっきりする.

宣言文では,[]の数がそのまま配列の次元数に対応する.

boolean     array0 = 1;                 //ただの変数
boolean[]   array1 = new boolean[10];   //1次元のboolean配列の宣言
boolean[][] array2 = new boolean[10][5];//2次元のboolean配列の宣言

しかし,実行文では逆に括弧の数が少ないときの方が次元数が高い状態を意味する.例えば2次元配列bがあったとしてつぎのように扱われる.

/*宣言の続き*/
boolean[][] b = new boolean[10][5]; //array2と同じサイズ

/*実行文では*/
array0 = b[1][2];//配列要素(座標1,2の中身)を表す
array1 = b[3];   //1次元配列(の3番め)を表す
array2 = b;      //2次元配列全体を表す

基本的な考え方として,宣言した時よりも[]の数を減らした配列名を実行文で使用すると,配列全体(あるいは1次元分)を表現しているとみなされる.(より正確には配列のアドレスを示している.)このため,array2とbは同じ型だし,array1とb[3]が同じ型として扱われるわけである.これはもちろん1次元配列でも事情は同じである.

/*宣言の続き*/
boolean[] c = new boolean[10]; //array1と同じサイズ

/*実行文では*/
array0 = c[1];//配列要素(1番めの中身)を表す
array1 = c;   //1次元配列全体を表す