processing: ArrayListのadd()で配列の要素が全部同じになった話

ArrayList::add()の様子がおかしい・・?

見事にハマったので感想をば。

ランダムウォーク - gotutiyan’s blog
このランダムウォークを実装しようとしたとき、円の軌跡を残すために前100フレーム分の座標をArrayListに格納して管理しようとしました。

このとき、以下のような実装をしたのです。(少し省略していますが)

ArrayList<PVector> prev = new ArrayList<PVector>();
prev.add(now); 
/*nowは現在の座標で、毎フレーム更新されます。このnowを毎フレームaddすることで、保存されていくはず。*/

add()は配列の一番後ろに値を挿入します。しかし、この実装ではnowをadd()する度に、配列の要素全てが今addしたばかりのnowになってしまったのです。
こうなる原因は、ArrayListにおけるのメモリ関連にありました。

ArrayListはアドレスを保存している

変数を宣言したからにはそれを保存するためのメモリを消費していて、このメモリの場所のことをアドレスと呼ぶのでした。ArrayListにおけるadd() では、このアドレスを格納していくようです。値を格納しているのではありません。C言語を嗜んだことがある人でしたら、このような違いです。

int x[];  //理想
int *x[];  //現実

つまり、上記の実装では、決して変わることのないnowのアドレスを、何回も追加していたことになります。当然、nowを更新すれば、それを参照している配列の要素すべての値が書き換わります。
これを防ぐためには、新たにメモリを取り直してから格納すれば良いです。

prev.add(new PVector(now.x,now.y));

このように、「new PVector()」を用いることで、アドレスを取り直して追加することができ、配列の要素が書き換わらなくなりました。

感想

C++vectorの感覚でやっていたら痛い目を見ましたね・・