Javaプログラミングにおいて、データの集合を扱うListインターフェースは最も頻繁に利用されるデータ構造の1つです。

Javaの進化に伴い、Listを初期化する方法は多岐にわたるようになりました。

古いJDKバージョンから使われている伝統的な手法から、Java 9以降で導入されたモダンな記述法、さらにはStream APIを活用した高度な初期化まで、選択肢は非常に豊富です。

開発現場では、単にリストを作成するだけでなく、「そのリストは後から要素を変更するのか(可変性)」「要素数は固定なのか(固定サイズ)」「パフォーマンスやメモリ効率は最適か」といった観点で最適な初期化手法を選択する必要があります。

本記事では、JavaにおけるList初期化の全手法を網羅し、それぞれの特徴や使い分けのポイントをプロフェッショナルな視点で詳しく解説します。

JavaにおけるList初期化の基本概念

JavaのListはインターフェースであるため、直接インスタンス化することはできません。

一般的には、その実装クラスであるArrayListLinkedListをインスタンス化して使用します。

初期化には大きく分けて、「空のリストを作成する」場合と、「初期要素を持たせた状態で作成する」場合の2パターンがあります。

また、作成されたリストが「可変(Mutable)」か「不変(Immutable)」かという性質も、プログラムの堅牢性に大きく関わります。

実装クラスによる標準的な初期化

最も基本的かつ汎用的な方法は、実装クラスを直接new演算子で生成する方法です。

Java
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 の特徴と使い方

Java
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()も追加されました。

Java
List<String> mutableList = new ArrayList<>();
mutableList.add("X");
List<String> copy = List.copyOf(mutableList); // 不変リストとしてコピー

伝統的な手法:Arrays.asList

Java 8以前から広く使われているのが、java.util.Arrays.asList()です。

配列をリスト形式でラップする手法です。

Arrays.asList の挙動

Java
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を作る」という手法が一般的です。

匿名サブクラス(ダブルブレース初期化)の回避

かつては以下のような「ダブルブレース初期化」と呼ばれる書き方がありました。

Java
// 非推奨の書き方
List<String> list = new ArrayList<String>() {{
    add("A");
    add("B");
}};

しかし、この方法はメモリリークの原因になったり、不要な匿名クラスが生成されたりするため、現代のJava開発では非推奨とされています。

推奨される可変リストの初期化

現在は、以下のようにList.ofなどで作成したリストをArrayListのコンストラクタにラップする方法が推奨されます。

Java
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までの整数を持つリストを生成する例です。

Java
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()メソッドが使用可能になりました。

Java
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()を使用する方が効率的です。

これは単一の不変インスタンスを再利用するため、無駄なメモリ消費を抑えられます。

Java
List<String> empty = Collections.emptyList();

※現在では List.of() (引数なし)も同様の効果を持ちますが、古いコードベースではよく見かけます。

Collections.singletonList(T)

要素が1つだけの不変リストを作成します。

Java
List<String> single = Collections.singletonList("Only One");

これも List.of("Only One") と同様ですが、singletonListnull を許容するという細かな違いがあります。

手法の比較表

各初期化手法の特性をまとめました。

用途に応じて最適なものを選択してください。

手法可変性サイズ変更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の機能を最大限に活用し、クリーンで効率的なコードを目指しましょう。