4.3 配列

さて、次は配列です。配列の英語名は“array”と言い、「整列」という意味があります。プログラミングの世界では、「変数のグループ」のことだと理解しておきましょう。
この配列は非常に便利な機能なのですが、つまずきやすいポイントでもあるようです。ある意味ここは正念場なので、まずは「概念を理解する」ことが大事になります。

4.3.1 1次元配列

まずは、配列が一体どういったものかを見てみましょう。この概念が理解できると使い方も自然に分かると思います。次の式を見てください。

int[] a = new int[3];    //int型の配列a(箱は3つ)を設定

a[0] = 1;    //a[0]に1を代入
a[1] = 10;    //a[1]に10を代入
a[2] = 4;    //a[2]に4を代入
println(a);    //aの値を下のメッセージウィンドウに出力

配列の宣言部分は次の式になります。int型の配列a(箱は3つ)を設定しています。

int[] a = new int[3];

また、次のようにも書けます。

int[] a = { 1, 10, 4 };

概念的には【図4.3-a】になります。

array1.gif
図4.3-a

このように、変数の箱が並んでいるイメージです。しかし、この箱はひとつひとつ独立しているのではなく、全部くっついています。この[ ]に囲まれた箱の番号をインデックス番号と呼びます。

ここで気をつけなければいけないのは、a[0]のように、インデックス番号は必ず0から始まります。
ですから、箱が3つあったらインデックス番号は0、1、2となります。

しかし、まだこれだけではなぜ配列という機能の存在が必要なのか分かりづらいと思います。そこで、次の式を見てください。

int[] a = new int[4];    //int型の配列a(箱は4つ)を設定

for(int i = 0; i < 4; i ++) {
 a[i] = i ;    //a[i]にiを代入
}

println(a);    //aの値を下のメッセージウィンドウに出力

この式の配列の中身は【図4.3-b】になります。

array2.gif
図4.3-b

この時点で、勘のいい人は分かるかもしれませんが、配列の利点とは、インデックス番号に変数が使えるということなのです。もし配列を使わなければ次のようになります。

int a0, a1, a2, a3;    //int型の変数を設定

a0 = 0;
a1 = 1;
a2 = 2;
a3 = 3;

println(a0);
println(a1);
println(a2);
println(a3);

長くなりますよね。配列の箱が4つだったらまだいいようなもの、これが100個になると非常に効率が悪いですよね。
どうですか?配列の便利さが分かってもらえたでしょうか?また、配列の番号に変数が使えるということは様々な計算式が使えるということです。
次のサンプルを見てください。

int[] a = new int[4];    //int型の配列a(箱は4つ)を設定

for(int i = 0; i < 4; i ++) {
 a[i] = i * 2 ;    //a[i]にi*2の値を代入
}

println(a);    //aの値を下のメッセージウィンドウに出力

この式の配列の中身は【図4.3-c】になります。

array3.gif
図4.3-c

4.3.2 各オブジェクトの値を格納する

配列を使う利点のひとつは、個々のオブジェクトの属性の値を格納できるという点です。次のサンプルは、円を表示するプログラムです。

ステップ1
まずはひとつから始めます。

2016_06_21_0_17
図4.3-d
リスト4.3-a
float eSize;    //円のサイズ

size(400, 400);
background(255);
noStroke();
fill(0);

eSize = random(10, 100);  //円のサイズを10~100未満で決定

//円を画面中央に描画
ellipse(width/2, height/2, eSize, eSize);

ステップ2
次に、ステップ1のコードをもとにして、配列を使わずに3つ表示させてみます。

2016_06_21_0_18
図4.3-e
リスト4.3-b
float eSize0, eSize1, eSize2;    //円のサイズ

size(400, 400);
background(255);
noStroke();
fill(0);

eSize0 = random(10, 200);  //円のサイズを10~100未満で決定
eSize1 = random(10, 200);
eSize2 = random(10, 200);

//円をx=0, 200, 400の位置に描画
ellipse(0, height/2, eSize0, eSize0);
ellipse(200, height/2, eSize1, eSize1);
ellipse(400, height/2, eSize2, eSize2);

ステップ3
それでは、ステップ2のコードに配列を使ったらどうなるのでしょう。かなり短くなります。

リスト4.3-c
float[] eSize = new float[3];    //円のサイズ

size(400, 400);
background(255);
noStroke();
fill(0);

for (int i = 0; i < 3; i ++) {

  //円のサイズを10~100未満で決定
  eSize[i] = random(10, 200);

  //円をx=0, 200, 400の位置に描画
  ellipse(i * 200, height/2, eSize[i], eSize[i]);
}

どうですか?for文と組み合わせると短くなります。配列3つではあまり差がありませんが、図形が100個などのケースでも、このコードだとそれほど長くなりません。

4.3.3 オブジェトの数を増やす

それでは、もう少し数を増やしてみましょう。次の例は10個円を作ったものです。

図4.3-f
リスト4.3-d
float[] eSize = new float[10];    //円のサイズ

size(550, 200);
background(255);
noStroke();
fill(0, 200);

for (int i = 0; i < 10; i ++) {

  eSize[i] = i * 5.0 + 50;    //円のサイズの初期値をそれぞれ設定
  ellipse(50 * i + 50, height/2, eSize[i], eSize[i]);
}

次に、円が拡大縮小するアニメーションにしてみましょう。円の大きさの初期値を変えると、連続した形がうねっているような動きを与えることができます。

2016_06_21_0_18
図4.3-g
リスト4.3-e
int num = 10;    //円の数
float[] eSize = new float[10];    //円のサイズ
float[] speed = new float[10];    //円の拡大縮小のスピード
float minSize = 50.0;     //円のサイズの最小値
float maxSize = 150.0;    //円のサイズの最大値

void setup() {
  size(550, 200);
  background(255);
  noStroke();
  fill(0, 200);

  for (int i = 0; i < num; i++) {    
    eSize[i] = i*5.0 + 50;    //円のサイズの初期値をそれぞれ設定
    speed[i] = 1.0;    //円のスピードの初期値
  }
}

void draw() {
  background(255);

  for (int i = 0; i < num; i ++) {
    eSize[i] += speed[i];    //円のサイズを変化させる
    //もし円のサイズが最大値を超えたか最小値未満になったら
    //speedの増減を反転させる
    if (eSize[i] > maxSize || eSize[i] < minSize)
      speed[i] = -speed[i];
      
    ellipse(50*i + 50, height/2, eSize[i], eSize[i]);
  }
}

4.3.4 マウスの動きを記録する

配列は、マウスの動きを記録するときにもよく使われます。次のサンプルは、ドラッグをしている間にマウスのx, y座標を配列に保存し、ドラッグが終わったらその配列に格納された座標とサイズに基づいて再生しています。

2016_06_21_0_36
図4.3-h
リスト4.3-f
float[] x = new float[100];  //円のx座標
float[] y = new float[100];  //円のy座標
float[] eSize = new float[100];  //円のサイズ
int counter = 0;  //配列に値を順番に入れるためのカウンター
boolean mouseFlag = false;  //マウスが押されているかを判定するフラッグ

void setup() {
  size(400, 400);
  background(255);
  noStroke();
  fill(0);

  //x[]、y[]、eSize[]を初期化
  for (int i = 0; i<x.length; i++) {
    x[i] = y[i] = eSize[i] = 0;
  }
}

void draw() {
  //background(255);

  //軌跡を残す場合は、下の3行をON、backgroundをOFFにする
  fill(255, 10);  //透明度のあるrectを描画
  rect(0, 0, width, height);
  fill(0);      //オブジェクトは黒

  //マウスが押されていなければ描画
  if (!mouseFlag) {

    ellipse(x[counter], y[counter], eSize[counter], eSize[counter]);

    counter ++;
    if (counter >= x.length-1) counter = 0;
  }
}

void mousePressed() {
  background(255);
  counter = 0;  //マウスが押されたらカウンターを初期化
  //x座標とy座標の配列を初期化
  for(int i = 0; i < x.length; i ++){
    x[i] = y[i] = 0;
  }
  mouseFlag = true;  //フラッグをON
}

void mouseDragged() {

  x[counter] = mouseX;  //マウスの座標を記録
  y[counter] = mouseY;

  //スピードを記録してeSizeにする
  eSize[counter] = dist(mouseX, mouseY, pmouseX, pmouseY)*2;
  counter ++;  //カウンターを加算

  //カウンターは配列のインデックス番号の最大値で止まる
  if (counter >= x.length-1) counter = x.length-1;
}

void mouseReleased() {
  mouseFlag = false;  //マウスフラッグはOFF
}