結論から言うと、迷ったらまずArrayListを選べば、ほとんどの場面で困りません。
先頭での追加・削除や、Iteratorで位置を保ったままの挿入・削除が頻発するときだけLinkedListを検討してください。これがJavaのListの使い分けの基本線になります。
とはいえ、その根拠と、LinkedListを選ぶべき場面の見分け方がわからなければ、現場のコードレビューで指摘されたときに答えられません。
この記事では、その判断基準をArrayList・LinkedList・Vector・Stackの内部構造の違いから示し、add・get・removeなどの基本操作、Stream APIとの組み合わせ、不変Listの作り方まで、コード例つきで解説します。
駆け出しのころ、筆者はこの使い分けを知らずにパフォーマンスの悪いコードを書いた経験があります。同じ回り道をしないための一本です。
この記事は、Java入門ロードマップの「STEP2|基本文法」に対応しています。全体の学習順は次の記事で確認できます。
JavaのListとは

JavaのListは、複数のデータ(要素)を順序付けて格納するための入れ物です。
公式には「コレクションフレームワーク」という、データを扱うための仕組みの一部として提供されています。
Listの基本概念と特徴
Listインターフェースは、データを扱う上で便利な3つの大きな特徴を持っています。
これらの特徴により、プログラムの実行中にデータ数が変動するような場面で、JavaのListは役立ちます。
Listのメリット・デメリット
便利なListにも、メリットとデメリットがあります。
メリット
- 要素数の変更が容易: アプリケーションの実行中にデータが増減しても柔軟に対応可能です。
- 豊富なメソッド: 要素の追加、削除、検索、並び替えなど、データを操作するための便利なメソッドが多数用意されています。
デメリット
- プリミティブ型を直接格納できない:
intやdoubleなどのプリミティブ型は直接格納できません。IntegerやDoubleといったラッパークラスへの変換が必要です。 - メモリ消費: 配列に比べて、付加的な情報(サイズなど)を保持するため、わずかに多くのメモリを消費します。
これらの特性を理解し、場面に応じて配列と使い分けるのが重要です。
Listの種類

JavaのListはインターフェース(設計図)であり、実際に使う際にはそれを実装した具象クラスのインスタンスを生成します。
ArrayListとは
ArrayListは、JavaのListの中で最もよく使われるクラスの一つです。内部的には配列を利用してデータを管理しています。
日常的な開発では、まずArrayListを検討してください。データの参照が主な用途の場合に特に強みを発揮するListです。
LinkedListとは
LinkedListは、各要素が前後の要素への参照(リンク)を持つことで、リスト構造を実現しています。データが数珠つなぎになっているイメージです。
先頭への追加・削除や、Iteratorを使った追加・削除が頻繁に発生する処理には、LinkedListが適しています。
VectorとStackの違いと使い分け
VectorとStackは、古いバージョンのJavaから存在するクラスです。
VectorはArrayListとよく似ていますが、スレッドセーフであるという大きな違いがあります。
つまり、複数のスレッドから同時にアクセスしても、データが壊れないように設計されています。
ただし、その分パフォーマンスはArrayListに劣るため、2026年時点でもCollections.synchronizedList()や、java.util.concurrentパッケージのCopyOnWriteArrayListでスレッドセーフを実現します。
StackはVectorを継承したクラスで、LIFO(Last-In, First-Out)、つまり「後入れ先出し」のデータ構造を実現します。
本を積み上げて、上から取っていくイメージです。push()で要素を追加し、pop()で要素を取り出します。
2026年時点でも、Stackの代わりにArrayDequeを使うのがJava公式Javadocでも明記されているベストプラクティスです。
連続したメモリに並ぶ
番号での取得は速い
途中の挿入・削除は要素をずらす
各要素が次の要素を指してつながる
途中の挿入・削除は付け替えるだけ
番号での取得は先頭から辿る
ArrayListとLinkedListの比較表で選ぶ
どのListを使うべきかの選び方
では、どのListを選べばよいのでしょうか。選択の基準はシンプルです。
- 要素の参照(取得)がメインで、追加・削除が少ない場合:
ArrayListを選びましょう。ほとんどのケースでこれが最適解です。 - リストの先頭への追加・削除や、Iteratorで位置を保ったままの追加・削除が頻繁に発生する場合:
LinkedListが適しています。 - マルチスレッド環境で、スレッドセーフなListが必要な場合:
ArrayListをCollections.synchronizedList()でラップするか、CopyOnWriteArrayListを検討します。 - スタック構造が必要な場合:
ArrayDequeを使用するのが現在のベストプラクティスです。
実務の感覚を付け加えると、最初からLinkedListを選ぶ判断をすることはほとんどありません。
要素数が数万件を超えるような規模でない限り、ArrayListの追加・削除コスト(後続要素のシフト)は体感できるほどの差になりにくく、むしろLinkedListが要素ごとに前後の参照を保持する分のメモリオーバーヘッドの方が問題になりやすいためです。
迷ったらArrayListから始め、プロファイリングで実際にボトルネックが見えてからLinkedListへの変更を検討する、という順序が安全です。
Listの使い方

ここからは、具体的なコードを見ながらJavaのListの使い方を学んでいきましょう。今回はArrayListを例に説明します。
Listの作成方法(ArrayList/LinkedListの初期化)
Listを生成する際は、Listインターフェースの変数に、ArrayListなどの具象クラスのインスタンスを代入します。
このようにインターフェース型で宣言しておくと、後からArrayListをLinkedListに変更したくなった場合でも、修正箇所を最小限に抑えられます。
現場でよくある勘違いとして、Arrays.asList()で作ったListが挙げられます。
これは配列を裏側に持つ「固定長のビュー」を返すため、get()やset()は使えても、add()やremove()を呼ぶとUnsupportedOperationExceptionが発生します。
可変長のListが必要な場合は、new ArrayList<>(Arrays.asList(...))のようにコンストラクタでラップしてください。
Listの要素の追加・削除・取得
Listの基本的な操作は、以下のメソッドで行います。
- 追加:
add() - 取得:
get() - 更新:
set() - 削除:
remove() - 要素数取得:
size() - 空かどうかの判定:
isEmpty()
Java 21以降では、ListがSequencedCollectionを実装するようになり、先頭・末尾要素へのアクセス専用メソッドが使えます。
get(0)やget(list.size() - 1)のようなインデックス計算をせずに済むため、意図が伝わりやすくなります。
イテレーションの方法(for文・for-each・Iterator)
Listに格納されたすべての要素を順番に処理するには、繰り返し処理(イテレーション)を使います。主に3つの方法があります。
拡張for文 (for-each)
最もシンプルで推奨される方法です。
従来のfor文
インデックスを使って要素にアクセスします。
Iterator
ループの最中に要素を安全に削除したい場合に使用します。
Listの並び替えと検索(Collectionsクラス活用)
Collectionsというユーティリティクラスを使うと、Listの並び替えや検索が簡単に行えます。
Listの応用テクニック

基本的な使い方をマスターしたら、次はより高度なテクニックを見ていきましょう。
Listのコピーとサブリストの作成
Listのコピー
元のListとは別に、同じ要素を持つ新しいListを作成したい場合があります。
サブリストの作成
subList()メソッドを使うと、Listの一部を切り出して新しいListとして扱えます。
subList()は元のリストの「ビュー」を返すため、取り扱いには注意が必要です。
特に、サブリスト取得後に元のリストの構造そのものを変更(add/remove)すると、サブリスト側の操作でConcurrentModificationExceptionが発生します。
また、List.of()で作った不変Listに対してsubList()を呼んでも例外にはなりませんが、返るビューも不変のままです。
そのビューに対してadd()やset()を呼んだ時点でUnsupportedOperationExceptionになります。
ListとStream APIの活用
Java 8で導入されたStream APIを使うと、Listの集計やフィルタリング処理を簡潔に記述できます。
for文で書くよりも、何をしているかが一目でわかりやすいのが特徴です。
なお、Java 16以降ではcollect(Collectors.toList())の代わりにtoList()を直接呼べる短縮記法が使えます。
ただし挙動は同じではありません。
collect(Collectors.toList())が返すListは、現在の標準的なJDK実装では可変(要素の追加・削除が可能)ですが、これはAPI仕様として保証された挙動ではありません。
一方Stream.toList()が返すListは仕様上明確に不変で、add()を呼ぶとUnsupportedOperationExceptionになります。
不変List(Immutable List)の作り方
一度作成したら要素を変更・追加・削除されたくない、読み取り専用のListを作ります。これを「不変List」と呼びます。
Java 9以降: List.of()
Java 8以前: Collections.unmodifiableList()
既存のListから不変Listを作る: List.copyOf()
List.of()が新規に要素を列挙して不変Listを作るのに対し、Java 10で追加されたList.copyOf()は既存のListをコピーして不変Listを作ります。
不変Listは、データを安全に保ちたい場合に有効なテクニックです。
Listを使う際の注意点

スレッド安全性の問題
ArrayListやLinkedListは、スレッドセーフではありません。
複数のスレッドから同時に同じListインスタンスにアクセスして変更を加えると、予期せぬエラー(ConcurrentModificationExceptionなど)やデータの不整合が発生する可能性があります。
マルチスレッド環境でListを扱う場合は、以下のいずれかの方法を検討してください。
Collections.synchronizedList()でListをラップするjava.util.concurrentパッケージのCopyOnWriteArrayListクラスを使用する
パフォーマンスに影響する操作
Listの種類によって、得意な操作と苦手な操作があります。
ArrayList: リストの先頭や中間への要素の追加・削除は、後続の全要素をシフトさせるため、要素数が多いほどパフォーマンスが著しく低下します。LinkedList: インデックスを指定した要素の取得(get(index))は、先頭または末尾の近いほうから順にたどるため、要素数が多いほど時間がかかります。
ループの中でこれらの操作を多用すると、アプリケーション全体の性能に影響を与える可能性があるため、注意が必要です。
Null要素や重複の取り扱い
Listはnullや重複した要素を許容します。
これは柔軟である一方、意図しないnullが紛れ込むとNullPointerExceptionの原因になります。
また、重複を許したくないデータを扱う場合は、Setインターフェースを使うか、add()する前にcontains()メソッドで存在チェックを行うなどの工夫が必要です。
よくある質問
Q1. JavaのListとArrayListの違いは何ですか?
A1. Listはインターフェース(設計図)で、ArrayListはそれを実装した具象クラスです。実際にコードで使うときは、List型の変数にArrayListのインスタンスを代入します。ArrayListはListの実装の一つに過ぎず、他にLinkedListやVectorも同じListインターフェースを実装しています。
Q2. ArrayListとLinkedList、どちらを使えばいいですか?
A2. 迷ったらArrayListを選んでください。要素の取得が速く、多くの場面で最適です。先頭への要素の追加・削除や、Iteratorで位置を保ったままの追加・削除が頻繁に発生する処理のときだけ、LinkedListを検討します。
Q3. Listで重複した値は格納できますか?
A3. はい、格納できます。Listは同じ値を持つ要素を複数格納できるデータ構造です。重複を許したくない場合は、SetインターフェースやListのcontains()メソッドでの事前チェックを使います。
Q4. Listはスレッドセーフですか?
A4. いいえ、ArrayListとLinkedListはスレッドセーフではありません。マルチスレッド環境ではCollections.synchronizedList()でラップするか、CopyOnWriteArrayListを使います。
Q5. Listを変更できないようにするにはどうすればいいですか?
A5. Java 9以降ならList.of()で新規の不変Listを作成できます。既存のListから不変Listを作りたい場合は、Java 10で追加されたList.copyOf()を使います。どちらも要素の追加・削除を試みるとUnsupportedOperationExceptionが発生します。
Q6. Stream APIでListを作るtoList()とcollect(Collectors.toList())の違いは何ですか?
A6. Java 16以降のtoList()が返すListは不変で、要素の追加・削除ができません。一方、collect(Collectors.toList())が返すListは可変です。用途に応じて使い分けてください。
まとめ
この記事では、JavaのListの基本概念から種類、使い方、応用テクニック、注意点までを解説しました。
- Listは順序を保持し、重複を許す動的なデータ構造
- 普段使いは
ArrayList、先頭での追加・削除が多いならLinkedListを選ぶ - 基本的な操作は
add,get,remove,setで行う - Stream APIや不変Listなどの応用テクニックも活用しよう
JavaのListは、あらゆる開発で登場する基本中の基本です。しかし、その特性をきちんと理解しているかどうかで、書けるコードの品質は大きく変わります。
| REV. | DATE | 内容 |
|---|---|---|
| 3 | 2026-10-04 | 記載内容の事実確認と修正 |
| 2 | 2026-08-10 | 2026年8月時点の情報で全体を見直し(古い記述を6件更新) |
| 1 | 2025-10-30 | 初版 |





