Java : List(リスト)の基本
List はコレクション・フレームワークの1つです。
 複数の要素を配列のように管理します。
 しかも、配列と違いサイズを気にする必要はありません。
本記事では、そんな List の基本的な使い方を解説していきます。
Listの概要
順序付けられたコレクションです。シーケンスとも呼ばれます。
このインタフェースのユーザーは、リスト内のどこに各要素が挿入されるかを精密に制御できます。
ユーザーは整数値のインデックス(リスト内の位置)によって要素にアクセスしたり、リスト内の要素を検索したりできます。
List は順序付けされたコレクションです。
 要素を追加するたびに 0, 1, 2, 3 ... とインデックスが割り振られて、厳密に順序が管理されます。
配列をイメージしていただくと分かりやすいかもしれません。
ただし、配列は生成するときに サイズを指定 する必要があります。
 一方、リストはサイズの指定が不要です。必要に応じて 自動で拡張 されていきます。
サイズを管理しなくてよい、というのが配列とリストの大きな違いであり、リストのメリットでもあります。
リストの例
// 生成時にサイズを指定する必要がありません。
final List<String> list = new ArrayList<>();
System.out.println(list); // []
System.out.println(list.size()); // 0
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
// サイズは自動で拡張されます。
System.out.println(list); // [aaa, bbb, ccc, ddd]
System.out.println(list.size()); // 4
 
      配列の例
// 生成時にサイズを指定する必要があります。
final String[] array = new String[3];
System.out.println(Arrays.toString(array)); // [null, null, null]
System.out.println(array.length); // 3
array[0] = "aaa";
array[1] = "bbb";
array[2] = "ccc";
System.out.println(Arrays.toString(array)); // [aaa, bbb, ccc]
System.out.println(array.length); // 3
// 配列のサイズが3なので、4つめの要素を書き込めません。
// 書き込もうとすると、非チェック例外の ArrayIndexOutOfBoundsException が発生します。
array[3] = "ddd"; // ArrayIndexOutOfBoundsException 発生
 
      サイズを管理しなくてよい、というのは重要です。
 プログラマが考えなければならないことが1つ減るからです。
 コードもシンプルになり、結果としてプログラムの品質も上がることが期待できます。
使い方の基本
それでは、Listの使い方をコード例で見ていきましょう。
 List には ArrayList を使います。(ArrayList の詳細については 後述 します)
リストの生成
public ArrayList()
初期容量10で空のリストを作成します。
ArrayList を new してリストを生成します。
 今回は、要素の型に String を使います。
final List<String> list = new ArrayList<>();
System.out.println(list); // []
 
      生成されたリストに要素はまだありません。
リストに要素を追加
boolean add(E e)
指定された要素をこのリストの最後に追加します(オプションの操作)。
List.add メソッドで、リストの末尾に要素を追加できます。
list.add("aaa");
System.out.println(list); // [aaa]
list.add("bbb");
System.out.println(list); // [aaa, bbb]
list.add("ccc");
System.out.println(list); // [aaa, bbb, ccc]
 
      コード例では、"aaa", "bbb", "ccc" の順で要素(文字列)を追加しています。
インデックスを指定して要素を参照
E get(int index)
このリスト内の指定された位置にある要素を返します。
List.get メソッドで、配列と同じようにインデックスで要素を参照できます。
System.out.println(list); // [aaa, bbb, ccc]
System.out.println(list.get(0)); // "aaa"
System.out.println(list.get(1)); // "bbb"
System.out.println(list.get(2)); // "ccc"
 
      指定したインデックスが範囲外だと、非チェック例外の IndexOutOfBoundsException が発生します。
// IndexOutOfBoundsException 発生
list.get(3);
 
      もし、すべての要素を先頭から順に参照したい場合は、拡張for文 が便利です。
for (final var item : list) {
    System.out.println(item);
}
// 結果
// ↓
// "aaa"
// "bbb"
// "ccc"
 
      上記のコードは、次の基本for文と同じに意味になります。
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
// 結果
// ↓
// "aaa"
// "bbb"
// "ccc"
 
      要素の更新
E set(int index, E element)
このリスト内の指定された位置にある要素を、指定された要素に置き換えます(オプションの操作)。
List.set メソッドで、指定したインデックスの要素を上書きします。
System.out.println(list); // [aaa, bbb, ccc]
list.set(0, "XXX");
System.out.println(list); // [XXX, bbb, ccc]
list.set(1, "YYY");
System.out.println(list); // [XXX, YYY, ccc]
list.set(2, "ZZZ");
System.out.println(list); // [XXX, YYY, ZZZ]
 
      要素の削除
E remove(int index)
このリスト内の指定された位置にある要素を削除します(オプションの操作)。 後続の要素を左に移動します(インデックスから1を減算)。
List.remove メソッドで、指定したインデックスの要素を削除します。
 削除した要素より後ろの要素は、1つ前へと詰められます。
System.out.println(list); // [XXX, YYY, ZZZ]
list.remove(0);
System.out.println(list); // [YYY, ZZZ]
 
      ここでご紹介した以外にも、List には便利なメソッドがいろいろあります。
 下記の API 使用例の記事もご参照ください。
Listの種類
List の代表的な実装には ArrayList と LinkedList があります。
 それぞれのクラスの簡単な特徴のまとめです。
| クラス | 末尾に追加 | インデックスによる参照 | インデックスによる挿入・削除 | 特徴 | 
|---|---|---|---|---|
| ArrayList | ◎(非常に高速) | ◎(非常に高速) | ×(非常に低速) | LinkedListに比べて全体的にパフォーマンスが良いです。 よって、基本的にはArrayListを使うことをおすすめします。  |  
         
| LinkedList | ○(高速) | ×(非常に低速) | △(普通) | ArrayListに比べて全体的にパフォーマンスは悪いです。 ただし、インデックスによる要素の挿入・削除はArrayListより高速です。 要素の挿入・削除を多用する場面では、LinkeListを使うことを検討しましょう。  |  
         
ArrayList
Listインタフェースのサイズ変更可能な配列の実装です。
Listの基本的な実装です。
 要素は、内部的には配列で管理されます。
ArrayList には容量(capacity)があり、最初はある程度余裕をもった容量で配列が作られます。
 そして、リストに要素を追加していき、容量いっぱいになったら自動的に配列が拡張されます。
※リストの "サイズ" と "容量" は別物なのでご注意ください。

リストを使う側は容量は気にしなくても問題ありません。
 ArrayList側がうまく管理してくれます。
基本的には配列なので、要素の末尾への追加やインデックスによる参照は非常に高速です。
反面、要素を削除すると後続の要素を1つ前にずらすという処理が入るため遅くなります。

LinkedList
ListおよびDequeインタフェースの二重リンク・リスト実装です。
LinkedList は要素を配列で管理しません。
 要素と要素をリンクでつなぐことによって順序を管理します。
イメージ図になります。
 例えば、LinkedList.get(2) で要素(c)を取得しようとすると、
- 先頭の要素(a)からリンク(次へ)をたどり、要素(b)へ。
 - 要素(b)からリンク(次へ)をたどり、要素(c)を取得。
 
となります。
LinkedList.get(2)は先頭からのほうが近いですが、LinkedList.get(4)など後尾からリンク(前へ)でたどったほうが近い場合は、そちらからたどっていきます。
LinkedList.get(100000)だと、100000回リンクをたどることになります。
 LinkedListのインデックス参照が非常に遅い理由がイメージできましたでしょうか。
リンクで要素にたどりつけさえすれば、そのインデックスへの要素の挿入や削除は高速です。
 リンクを張り替えるだけで済むからです。
インデックス2の要素(c)を削除するイメージです。
ただし、リストの真ん中あたりの要素を削除するには、結局その要素までリンクをたどることになり、そこまで高速ではありません。
 そのあたりはご注意ください。
補足
List vs. 配列
リストと配列は似たような用途で使われます。
 それではリストと配列どちらを使った方がよいのでしょうか…?
おすすめはリストです。
- サイズの管理をしなくてい。
 - 不変(イミュータブル) なリストも作れる。
 - スレッドセーフなリストも作れる。
 
などなど、優れているところが多いです。
あえて配列を使いたいケースは、
- パフォーマンス最重視
 - プリミティブ型( int や double など) を使いたい
 
という感じでしょうか。
配列については下記の記事もご参照ください。
Vectorについて
このクラスは、Java 2プラットフォームv1.2の時点でListインタフェースを実装するように改良された結果、Java Collections Frameworkのメンバーとなりました。
新しいコレクションの実装とは異なり、Vectorは同期をとります。
スレッドセーフな実装が必要ない場合は、Vectorの代わりにArrayListを使用することをお薦めします。
List の実装に Vectorクラスがあります。
 ただし、Vector は古いAPIです。
もしスレッドセーフなリストが必要な場合は、Vector ではなく Collections.synchronizedList を使うことをおすすめします。
public static
List synchronizedList(List list) 
指定されたリストに連動する同期(スレッドセーフな)リストを返します。
ArrayList でも LinkedList でもスレッドセーフにできるので、こちらを使っていきましょう。
他のコレクション
List 以外のコレクションには、Set や Map、Queue などがあります。
 本記事で紹介した LinkedList は、実は Queue の実装でもあります。
もし興味がありましたら、関連記事やAPI仕様などをご確認ください。
関連記事
- 配列 (Array) の使い方
 - 拡張for文 (for-eachループ文)
 - Map(マップ)の基本
 - Set(セット)の基本
 - List の初期化方法いろいろ
 - Map の初期化方法いろいろ
 - Set の初期化方法いろいろ
 - 配列 vs. List
 - 配列からListへの変換
 - List から配列への変換
 
- API 使用例 
        
- Collection (コレクション)
 - Collections (コレクション操作)
 - Comparable
 - Comparator
 - Iterator
 - List (リスト)
 - Map (マップ)
 - Map.Entry (キーと値のペア)
 - Queue (キュー) 
          
- AbstractQueue
 - ArrayBlockingQueue (ブロッキング・配列キュー)
 - ArrayDeque (両端キュー)
 - BlockingDeque (ブロッキング・両端キュー)
 - BlockingQueue (ブロッキング・キュー)
 - ConcurrentLinkedDeque (並列処理用・両端キュー)
 - ConcurrentLinkedQueue (並列処理用キュー)
 - Deque (両端キュー)
 - LinkedBlockingDeque (ブロッキング・リンク両端キュー)
 - LinkedBlockingQueue (ブロッキング・リンクキュー)
 - LinkedList (二重リンク・リスト)
 - PriorityBlockingQueue (ブロッキング・優先度付きキュー)
 - PriorityQueue (優先度付きキュー)
 
 - RandomAccess
 - Set (セット)
 - Spliterator
 
 



