Arrays.asList(T... a) は、なぜ add や remove ができないのか?

Arrays.asList(T... a) というメソッドについてググると、「このメソッドで返ってくる List は、addremove ができないので注意しましょう」ということが良く書かれています。
でも、「なぜできないのか?」という点については書かれていないことが多いので、理由を説明します。


一言でまとめると「配列を List として扱えるようにラップするためのものだから」です。
(「変換するもの」ではないです)

そもそも、どう使うもの?

配列を List として扱いたいときに使います。


例えば、シャッフルするのに Collections.shuffle​(List list) という引数に List を取るメソッドはあるのですが、引数に配列を取るメソッドがありません。
そういうときに、 Collections.shuffle(Arrays.asList(a)) という風にラップすることで、引数に配列を渡せるようになります。


Arrays.asList(T... a) で返ってくるリストは元になった配列と連動しているので、この List の要素を書き換えれば配列の要素も書き換わります。

String[] array = new String[] {
    "aaa", "bbb", "ccc", "ddd", "eee"
};

// 配列を List にラップしてシャッフルする
Collections.shuffle(Arrays.asList(array));

// 元の配列が、ちゃんとシャッフルされている
// => [ccc, eee, ddd, bbb, aaa]
System.out.println(Arrays.toString(array));


Arrays.asList(T... a) が返すリストクラスの実装を見てみると、配列をラップしているだけなのがよく分かると思います。 ((この ArrayListjava.util.ArrayList とは別のクラスです。))

private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable
{
    private final E[] a;

    ArrayList(E[] array) {
        a = Objects.requireNonNull(array);
    }

    @Override
    public int size() {
        return a.length;
    }

    @Override
    public E get(int index) {
        return a[index];
    }

なぜ add や remove ができないのか?

もし addremove を実装しようとすると、元になった配列の長さを変える必要があります。
しかし、Java では配列の長さを変えられません。


そのため、setget のような配列の中身に対して操作するメソッドは実装されていますが、addremove のような配列自体を変更する必要のあるメソッドは実装されていないのです。((正確には List インタフェースは addremove を実装する必要があるので、メソッドはあるけど UnsupportedOperationException をスローするという処理になっています)))

まとめ

明日の Java アドベントカレンダー、担当は null さんです。
誰か!書いて!!!


追記:
@opengl-8080 さんが書いてくださいました!
Doma2 でログ出力を制御する - Qiita