Java Tips | コレクション:List初期化

Java Java
スポンサーリンク

List初期化は「最初の一歩で“性質”を決める」作業

Listの初期化は、単に「空のリストを作る」「要素入りのリストを作る」だけの話に見えますが、
実はこの瞬間に「変更できるか」「スレッドセーフか」「固定サイズか」といった性質が決まります。

業務コードで一番多いバグの一つが、
「あとで要素を追加したいのに、変更できないListを作ってしまった」
「nullを返してしまって、呼び出し側が毎回nullチェックしている」
といった“初期化の設計ミス”です。

だからこそ、「どういう用途のListなのか」を意識して、
それに合った初期化パターンをユーティリティとして持っておくと、
コードの質が一気に安定します。


一番基本:空の可変Listを作る(new ArrayList<>())

「あとで中身を変える前提」の標準形

業務で一番よく使うのは、「最初は空だけど、あとで要素を追加していくList」です。
この場合は、素直に new ArrayList<>() を使うのが基本です。

import java.util.ArrayList;
import java.util.List;

public class ListInitBasic {

    public static void main(String[] args) {
        List<String> names = new ArrayList<>();

        names.add("山田");
        names.add("佐藤");

        System.out.println(names); // [山田, 佐藤]
    }
}
Java

ここで押さえておきたい重要ポイントは二つです。

一つ目は、「変えられるList(可変List)を作っている」ということです。
addremove が自由にできるので、「あとから増える・減る」前提のデータに向いています。

二つ目は、「変数の型は List、実体は ArrayList」という書き方にしていることです。
List<String> names = new ArrayList<>(); のように、
インターフェース型(List)で受けておくと、
あとで実装を LinkedList などに変えたくなったときも、
呼び出し側のコードをほとんど変えずに済みます。


「最初から中身が決まっているList」を作る

Arrays.asList と List.of の違いを理解する

「最初から要素が決まっていて、すぐに使いたい」という場面も多いです。
例えば、固定のコード一覧や、テストデータなどです。

ここでよく使われるのが Arrays.asListList.of です。

import java.util.Arrays;
import java.util.List;

public class ListInitFixed {

    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("A", "B", "C");
        List<String> list2 = List.of("A", "B", "C");

        System.out.println(list1); // [A, B, C]
        System.out.println(list2); // [A, B, C]
    }
}
Java

見た目は同じですが、性質が違います。
ここをきちんと理解しておくことがとても大事です。


Arrays.asList の性質:「サイズ固定」「要素の変更はできる」

add/remove はできないが set はできる

Arrays.asList("A", "B", "C") で作られるListは、
「配列をラップした固定サイズのList」です。

List<String> list = Arrays.asList("A", "B", "C");

list.set(0, "X");      // OK
System.out.println(list); // [X, B, C]

list.add("D");         // ここで例外(UnsupportedOperationException)
list.remove(1);        // ここでも例外
Java

重要なのは、「サイズは変えられないが、中身の書き換えはできる」という中途半端な性質です。

このせいで、次のような事故が起きやすくなります。

「固定のマスタとして使いたかったのに、どこかで set されて書き換えられてしまった」
「テストで add したら例外が飛んで驚いた」

つまり、Arrays.asList は「配列をListとして一時的に扱いたい」用途には便利ですが、
「業務ロジックで普通に使うList」としては、性質をよく理解していないと危険です。


List.of の性質:「完全に不変(変更不可)」

add/remove/set すべて禁止の“読み取り専用List”

Java 9以降で使える List.of は、
「完全に変更できない不変List」を作ります。

List<String> list = List.of("A", "B", "C");

System.out.println(list); // [A, B, C]

list.set(0, "X");   // 例外(UnsupportedOperationException)
list.add("D");      // 例外
list.remove(1);     // 例外
Java

ここでの重要ポイントは、「一切変更できない」ということです。
これは逆に言えば、「絶対に書き換えられない保証がある」ということでもあります。

業務で「定数的な一覧」を扱うとき、
例えば「ステータスの候補」「固定のコード一覧」などは、
List.of で作っておくと安心です。

public final class Statuses {

    private Statuses() {}

    public static final List<String> VALID_STATUSES =
            List.of("NEW", "IN_PROGRESS", "DONE");
}
Java

こうしておけば、どこかのコードがうっかり add しても例外になり、
「勝手に一覧が変わってしまう」という事故を防げます。


「可変Listが欲しいけど、初期値も入れたい」場合の定番パターン

List.of や Arrays.asList から ArrayList にコピーする

よくあるのが、
「最初からいくつか要素を入れておきたいが、その後も add/remove したい」
というパターンです。

この場合は、「不変Listを元にして、可変Listを作る」という書き方が定番です。

import java.util.ArrayList;
import java.util.List;

public class ListInitMutable {

    public static void main(String[] args) {
        List<String> base = List.of("A", "B", "C");
        List<String> list = new ArrayList<>(base);

        list.add("D");
        System.out.println(list); // [A, B, C, D]
    }
}
Java

あるいは、Arrays.asList からでも同じです。

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
list.add("D");
Java

ここでの重要ポイントは、「外から見える型は List、実体は ArrayList」というスタイルを守りつつ、
「初期値入りの可変List」を安全に作っていることです。


null を返さない List初期化ユーティリティという発想

「空のListを返す」ことをルールにするとコードが安定する

業務コードでよくある悪いパターンが、
「要素がなければ null を返す」というメソッドです。

// 悪い例
List<String> findNames() {
    if (条件に合うものがなければ) {
        return null;
    }
    ...
}
Java

これをやると、呼び出し側は毎回こう書く必要が出てきます。

List<String> names = findNames();
if (names != null) {
    for (String n : names) {
        ...
    }
}
Java

null チェックの書き忘れがバグの温床になります。

そこで、「Listを返すメソッドは、要素がなくても必ず空のListを返す」というルールにしてしまうと、
呼び出し側がとても楽になります。

import java.util.Collections;
import java.util.List;

public final class ListUtils {

    private ListUtils() {}

    public static <T> List<T> emptyIfNull(List<T> list) {
        return (list == null) ? Collections.emptyList() : list;
    }
}
Java

使い方はこうです。

List<String> names = ListUtils.emptyIfNull(findNames());
for (String n : names) {
    ...
}
Java

ここでの重要ポイントは、「null を“空のList”に正規化する」という発想です。
これにより、「nullかもしれないList」と「空のList」を区別しなくてよくなり、
コードの分岐が減ります。


まとめ:List初期化で本当に意識してほしいこと

List初期化は、単なる new ArrayList<>()List.of(...) の話ではなく、
「このListは変わるのか、変わらないのか」「nullを返すのか、空を返すのか」
といった設計の話です。

可変Listが欲しいなら new ArrayList<>() を基本形にする。
固定の一覧なら List.of(...) で不変Listにしておく。
初期値入りの可変Listが欲しいなら new ArrayList<>(List.of(...)) のようにコピーする。
メソッドの戻り値としては「nullではなく空のListを返す」ルールにしておく。

もしあなたのコードのどこかに、

if (list == null) { ... }
Java

というnullチェックが何度も出てくるなら、
それを「List初期化とnull正規化のユーティリティ」で置き換えられないか、一度眺めてみてください。

その小さな意識の変化が、
「コレクションの性質を理解して、安定した業務コードを書けるエンジニア」への、
確かな一歩になります。

タイトルとURLをコピーしました