perlっぽいListを作ったわけですが、3つほど考えておくべき点があります。
『AbstractListを使ったこと』
AbstractListで楽チン実装してしまいましたが、パフォーマンス的にはあまりよくないかもしれません。PerlLikeListの全てのメソッドはPerlLikeListのget, add, set, remove, sizeに集中し、それは結果としてラップされたListのget, add, set, removeに集中することとなります。よって、ラップされたListの他のメソッドが呼ばれることはありません*1。5つのメソッドに処理を集中出来ることは、ラップの確実性を高めるためには有利です。
しかし、AbstractListの実装はこれら5つのメソッドを組み合わせただけなので、パフォーマンス的にはあまりよくありません。例えば、clear()はremove()を繰り返し呼ぶだけです。ラップされたListのclear()は素晴らしい実装をしているかもしれませんが、それは生かされなくなってしまいます。
『車輪の再発明』
Jakarta Commons Collectionsに、似たような発想のクラスがすでにあります。get()に関する拡張はLazyList、set()系に関する拡張はGrowthListがやってくれます。
import org.apache.commons.collections.functors.ConstantFactory; import org.apache.commons.collections.list.GrowthList; import org.apache.commons.collections.list.LazyList; List<Integer> data = LazyList.decorate(new GrowthList(), ConstantFactory.NULL_INSTANCE);
こんな感じなのですが、get()しただけで配列サイズが大きくなってしまう点と、負の値を受け入れない点で作成したPerlLikeListとは動作が違います。
負値に関しては、微妙に違いますがLoopingListIteratorなんてものがあります。これは別に負値のindexを扱うクラスではないですが、添字0の要素から最後の要素へ戻ることが出来る循環型のイテレータです。
『ListインタフェースとPerlLikeListクラス』
この実装を書くきっかけとなった最初のコードに従えば、
List<Integer> data = new PerlLikeList<Integer>(); System.out.println(data.get(-2));
とPerlLikeListを使うことになります。これは、できるだけ上位のインタフェースで変数を定義すると言うセオリーに従った物ですが、この場合はあまり良くないんじゃないかと思います。
なぜなら、Listインタフェースは負の値が来ることを想定していないため、通常の実装ではget(-2)はExceptionを吐くからです。つまり、このロジックはdataをListとして定義しておきながら、PerlLikeList以外では決して動作しません。それならいっそのこと、
PerlLikeList<Integer> data = new PerlLikeList<Integer>(); System.out.println(data.get(-2));
と書いた方が、混乱が起こらなくていいのではないでしょうか?*2
なんでもかんでも上位のインタフェースで定義するのではなく、上位のインタフェースで定義するからにはその作法は守り、守れないときは無理に上位インタフェースとしないほうがいいと思います。
そんなわけで、無駄に4回に別れたシリーズもこれで完結です。たかがListの実装ですが、色々と考察してみるのは楽しいものですね(ノ*゜ー゜)ノ