Javaプログラミングにおいて、データの集合を扱うListインターフェースは最も頻繁に利用されるデータ構造の1つです。
Javaの進化に伴い、Listを初期化する方法は多岐にわたるようになりました。
古いJDKバージョンから使われている伝統的な手法から、Java 9以降で導入されたモダンな記述法、さらにはStream APIを活用した高度な初期化まで、選択肢は非常に豊富です。
開発現場では、単にリストを作成するだけでなく、「そのリストは後から要素を変更するのか(可変性)」「要素数は固定なのか(固定サイズ)」「パフォーマンスやメモリ効率は最適か」といった観点で最適な初期化手法を選択する必要があります。
本記事では、JavaにおけるList初期化の全手法を網羅し、それぞれの特徴や使い分けのポイントをプロフェッショナルな視点で詳しく解説します。
JavaにおけるList初期化の基本概念
JavaのListはインターフェースであるため、直接インスタンス化することはできません。
一般的には、その実装クラスであるArrayListやLinkedListをインスタンス化して使用します。
初期化には大きく分けて、「空のリストを作成する」場合と、「初期要素を持たせた状態で作成する」場合の2パターンがあります。
また、作成されたリストが「可変(Mutable)」か「不変(Immutable)」かという性質も、プログラムの堅牢性に大きく関わります。
実装クラスによる標準的な初期化
最も基本的かつ汎用的な方法は、実装クラスを直接new演算子で生成する方法です。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class BasicInitialization {
public static void main(String[] args) {
// ArrayListによる初期化(最も一般的)
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Spring");
// LinkedListによる初期化(要素の挿入・削除が頻繁な場合)
List<String> linkedList = new LinkedList<>();
linkedList.add("Python");
linkedList.add("Django");
System.out.println("ArrayList: " + arrayList);
System.out.println("LinkedList: " + linkedList);
}
}
ArrayList: [Java, Spring]
LinkedList: [Python, Django]
この方法で作成されたリストは可変(Mutable)であり、後から自由に要素を追加・削除・変更することが可能です。
特に理由がない限り、ランダムアクセス性能に優れたArrayListを選択するのがJavaのベストプラクティスとされています。
Java 9以降の標準:List.ofによる不変リストの生成
Java 9で導入されたList.of()メソッドは、現代のJava開発におけるデファクトスタンダードな初期化手法です。
非常に簡潔な構文で、要素を持ったリストを生成できます。
List.of の特徴と使い方
import java.util.List;
public class ListOfExample {
public static void main(String[] args) {
// 要素を指定して不変リストを生成
List<String> immutableList = List.of("Apple", "Banana", "Cherry");
System.out.println("List: " + immutableList);
// 要素を変更しようとすると例外が発生する
try {
immutableList.add("Orange");
} catch (UnsupportedOperationException e) {
System.out.println("エラー: 要素の追加はできません(不変リストです)");
}
}
}
List: [Apple, Banana, Cherry]
エラー: 要素の追加はできません(不変リストです)
List.of()で生成されるリストは不変(Immutable)です。
作成後にadd()やremove()、set()を呼び出すと、実行時にUnsupportedOperationExceptionがスローされます。
List.of を使用する際の注意点
- nullを許可しない
要素に
nullを含めることはできず、含めるとNullPointerExceptionが発生します。- 省メモリ
内部的に最適化されたクラスが使用されるため、
ArrayListよりもメモリ消費が少なくなります。- 固定サイズ
要素数も中身も変更できないため、設定値や定数リストの定義に適しています。
List.copyOf によるコピー生成
Java 10からは、既存のコレクションから不変リストを作成するList.copyOf()も追加されました。
List<String> mutableList = new ArrayList<>();
mutableList.add("X");
List<String> copy = List.copyOf(mutableList); // 不変リストとしてコピー
伝統的な手法:Arrays.asList
Java 8以前から広く使われているのが、java.util.Arrays.asList()です。
配列をリスト形式でラップする手法です。
Arrays.asList の挙動
import java.util.Arrays;
import java.util.List;
public class ArraysAsListExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Red", "Green", "Blue");
// 要素の書き換えは可能
list.set(0, "Yellow");
System.out.println("List: " + list);
// サイズの変更(追加・削除)は不可
try {
list.add("White");
} catch (UnsupportedOperationException e) {
System.out.println("エラー: サイズの変更はできません");
}
}
}
List: [Yellow, Green, Blue]
エラー: サイズの変更はできません
Arrays.asList()は「固定サイズ」のリストを生成します。
要素の値を書き換えることは可能ですが、リストの長さを変える操作(add/remove)はできません。
また、List.of()とは異なり、要素にnullを許容するという違いがあります。
可変リストとして初期化するテクニック
前述のList.of()やArrays.asList()は便利ですが、初期化後に要素を追加・削除したい場合には適していません。
その場合は、「不変リストをコンストラクタに渡してArrayListを作る」という手法が一般的です。
匿名サブクラス(ダブルブレース初期化)の回避
かつては以下のような「ダブルブレース初期化」と呼ばれる書き方がありました。
// 非推奨の書き方
List<String> list = new ArrayList<String>() {{
add("A");
add("B");
}};
しかし、この方法はメモリリークの原因になったり、不要な匿名クラスが生成されたりするため、現代のJava開発では非推奨とされています。
推奨される可変リストの初期化
現在は、以下のようにList.ofなどで作成したリストをArrayListのコンストラクタにラップする方法が推奨されます。
import java.util.ArrayList;
import java.util.List;
public class MutableInitialization {
public static void main(String[] args) {
// List.of で作成した不変リストを元に、可変な ArrayList を生成
List<String> mutableList = new ArrayList<>(List.of("One", "Two", "Three"));
// 要素の追加が可能
mutableList.add("Four");
System.out.println("Mutable List: " + mutableList);
}
}
Mutable List: [One, Two, Three, Four]
この書き方は、コードの可読性が高く、かつ意図した通りに可変なリストを安全に生成できるため、非常にバランスの取れた手法です。
Stream APIを活用した高度な初期化
Java 8で導入されたStream APIを使用すると、複雑な条件や加工を伴う初期化を宣言的に記述できます。
特に、連続した数値のリストや、既存のデータソースを加工してリスト化する場合に威力を発揮します。
数値範囲による初期化
1から10までの整数を持つリストを生成する例です。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class StreamInitialization {
public static void main(String[] args) {
// 1から10までのリストを生成
List<Integer> numbers = IntStream.rangeClosed(1, 10)
.boxed()
.collect(Collectors.toList());
System.out.println("Numbers: " + numbers);
}
}
Java 16以降の簡略化:Stream.toList()
Java 16からは、.collect(Collectors.toList())の代わりに、より簡潔な.toList()メソッドが使用可能になりました。
List<String> filteredList = List.of("apple", "banana", "apricot")
.stream()
.filter(s -> s.startsWith("a"))
.toList(); // Java 16+ の簡潔な書き方
注意点として、Stream.toList()が返すリストは不変(Immutable)に近い性質(正確には変更不可な実装)を持つため、可変なリストが必要な場合は、引き続きCollectors.toList()を使うか、コンストラクタでラップする必要があります。
特殊な初期化:空のリストと単一要素のリスト
特定の用途において、メモリ効率を最大化するための専用メソッドがjava.util.Collectionsクラスに用意されています。
Collections.emptyList()
要素が0個のリストが必要な場合、new ArrayList<>()を呼び出すよりも、Collections.emptyList()を使用する方が効率的です。
これは単一の不変インスタンスを再利用するため、無駄なメモリ消費を抑えられます。
List<String> empty = Collections.emptyList();
※現在では List.of() (引数なし)も同様の効果を持ちますが、古いコードベースではよく見かけます。
Collections.singletonList(T)
要素が1つだけの不変リストを作成します。
List<String> single = Collections.singletonList("Only One");
これも List.of("Only One") と同様ですが、singletonList は null を許容するという細かな違いがあります。
手法の比較表
各初期化手法の特性をまとめました。
用途に応じて最適なものを選択してください。
| 手法 | 可変性 | サイズ変更 | nullの許容 | 推奨JDKバージョン |
|---|---|---|---|---|
new ArrayList<>() | 可変 | 可能 | 許容 | 全世代 |
List.of(...) | 不変 | 不可 | 不可 | Java 9以降 |
Arrays.asList(...) | 要素のみ可変 | 不可 | 許容 | 全世代 |
new ArrayList<>(List.of(...)) | 可変 | 可能 | 不可 | Java 9以降 |
Stream.toList() | 不変 | 不可 | 許容 | Java 16以降 |
パフォーマンスとメモリの考慮事項
大量のリストを生成するアプリケーションでは、初期化方法がパフォーマンスに影響を与えることがあります。
- ArrayListの初期容量(Capacity)
ArrayListを空で作成し、後から数万件の要素を追加する場合、内部配列の再確保(リサイズ)が何度も発生し、処理が重くなります。あらかじめ要素数が予測できる場合は、初期容量を指定しましょう。
<pre>List<String> list = new ArrayList<>(10000); // 1万件分の領域を確保</pre>
- 不変リストのメリット
List.of()で生成されるリストは、内部的にフィールドとして要素を保持する特殊なクラス(List12, ListNなど)が使用されます。これらは通常の
ArrayListに比べてオーバーヘッドが極めて小さく、ガベージコレクション(GC)の負荷軽減にも寄与します。
まとめ
JavaのList初期化は、言語の進化とともに「より安全で、より簡潔に」書けるよう進化してきました。
現在主流の使い分けを整理すると、以下のようになります。
- 固定の要素を持ち、変更しない場合
List.of()を使用する。- 初期要素を持ち、後から変更(追加・削除)したい場合
new ArrayList<>(List.of())を使用する。- 空のリストから始め、順次要素を追加する場合
new ArrayList<>()を使用する。- nullを要素に含める必要がある場合
Arrays.asList()や通常のArrayListを検討する。
特に「不変性(Immutability)」を意識した設計は、マルチスレッド環境での安全性を高め、意図しないバグを防ぐために非常に重要です。
最新のJavaの機能を最大限に活用し、クリーンで効率的なコードを目指しましょう。






