【初心者必見】ArrayListはなぜ、List型で宣言されているのか?
Listインタフェース
実は皆さんが目にするArrayListのプログラムは上記のように
ArrayList<String> list = new ArrayList<>();
と宣言されているものよりも
List<String> list = new ArrayList<>();
のようにListインタフェース型の変数宣言されたものが多いと思います。
例えば以下のサンプルプログラムExample05はListインタフェース型で宣言しています。
package chap13;
import java.util.ArrayList;
import java.util.List;
public class Example05 {
public static void main(String[] args) {
List<Product> list = new ArrayList<>();
list.add(new Product("pencil", 100));
list.add(new Product("eraser", 60));
list.add(new Product("note", 80));
for (Product product : list) {
System.out.println(product);
}
}
}
package chap13;
public class Product {
private String productName;
private int price;
public Product(String productName, int price) {
this.productName = productName;
this.price = price;
}
@Override
public String toString() {
return this.productName + ":" + this.price;
}
}
<実行結果>
pencil:100 eraser:60 note:80 |
なぜ、このような書き方ができるのかというと、ArrayListクラスがListインタフェースを実装しているからですね。インタフェースで学んだようにインタフェースを実装したクラスのインスタンスはインタフェース型の参照に代入できるのでした。
では、なぜこのような書き方をするのかというと、プログラム中でListインタフェースを実装した他のクラスに付け替えることができるのです。例えば、同じListインタフェースを実装しているLinkedListクラスに付け加えることで配列の先頭要素の削除は高速になります。
このあたりの継承関係は少し複雑なのですが、この記事に関連する部分だけをピックアップすると下図のようになっています。

上図にあるLinkedListは「アルゴリズムとデータ構造」で学ぶ双方向リストです。そのため、削除の際にはポインタの付替えだけで済むため、要素の移動がなく高速なのです。
それに対して、ArrayListは内部的には配列を利用したリスト構造であるため先頭要素を削除すると、配列を前に詰めるようにコピーするため遅いのです。(下図参照)

次のサンプルプログラムで検証してみましょう。
実験のアイディアは、リストに10,000の要素(pencil)を詰めた配列を用意して、前半の半分の5,000個はArrayListとして、後半の半分の5,000個はLinkedListとして、配列の先頭要素から削除【remove】していって、すべての要素を削除し終わった時間をナノ秒単位で計測して比較するというものです。
計測にはナノ秒単位まで測れるSystem.nanoTime()メソッドを使っています。
また、以下のコードでArrayListをLinkedListに変換しています。
List<Product> list2 = new LinkedList<>(list1);
このようにコンストラクタを使って付け替えを行っている箇所を読み取って下さい。
package chap13;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Example06 {
public static void main(String[] args) {
List<Product> list1 = new ArrayList<>();
long startTime, endTime;
for (int i = 0; i < 10_000; i++) {
list1.add(new Product("pencil", 100));
}
startTime = System.nanoTime();
for (int i = 0; i < 5_000; i++) {
list1.remove(0);
}
endTime = System.nanoTime();
System.out.printf("%s%,7d%s", "ArrayListからの削除\t", (endTime - startTime), "ナノ秒かかりました");
System.out.println("");
List<Product> list2 = new LinkedList<>(list1);
startTime = System.nanoTime();
for (int i = 0; i < 5_000; i++) {
list2.remove(0);
}
endTime = System.nanoTime();
System.out.printf("%s%,9d%s", "LinkedListからの削除\t", (endTime - startTime), "ナノ秒かかりました");
}
}
<結果の例>
ArrayListからの削除 5,531,600ナノ秒かかりました LinkedListからの削除 440,100ナノ秒かかりました |
このようなことができるのもArrayListとLinkedListが両方ともListインタフェースを実装しているからです。そして、どちらのコンストラクタも引数としてList型を受け取れるようになっているからです。
ポリモーフィズムを使った引数設計でしたね。たとえるなら、「お母さんアレ取って」と言って理解しあえる夫婦のようなものでした。“アレー”リストなだけに。。。
ただし、繰り返しになりますが、動的な配列ではArrayListを使えば大丈夫で、LinkedListの活用場面はそれほどありません。
セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク
投稿者プロフィール

- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。