Arrays.asList() で生成した ArrayList が add() を使えないワケ

割と初歩的なことかもしれませんが、なるほどーと思ったのでメモします。

Javaで配列を `List<T>` に変換したい場合、

```java
String ids[] = new String[]{"a1", "a2", "a3"};
List<String> idList = Arrays.asList(ids);
```

って書くと思います。

でもこのままだと `add` ができないので（addメソッドを呼ぶと `UnsupportedOperationException` がスローされる）、

```java
String ids[] = new String[]{"a1", "a2", "a3"};
List<String> idList = new ArrayList<>(Arrays.asList(ids));
```

という具合に、生成した `ArrayList` をさらに `ArrayList` のコンストラクタに渡して `new` する、というコードを書くことがあるかと思います。

この記事では、なんで `Arrays.asList()` で生成した `ArrayList` は `add` ができないのに、それを `ArrayList` のコンストラクタの引数に渡すだけで `add` できるようになるのか、という疑問について、Java SEのソースコードを見ながら考えてみます。

# 2つのArrayList<E>クラス

まず、実は `ArrayList<E>` というクラスはJava SEの中に2つあります。
一つ目が `java.util.ArrayList<E>` で、二つ目が `java.util.Arrays$ArrayList<E>` です。名前は同じですが、定義されている場所が違うので全く別のクラスです。

後者の `java.util.Arrays$ArrayList<E>` は `Arrays` クラスのprivateな内部クラスですので、普通に `new` することはできません。

つまり、単純に

```java
List<String> idList = new ArrayList<>();
```

と書いたときに生成されるのは前者の `java.util.ArrayList<E>` になります。

そして、

```java
String ids[] = new String[]{"a1", "a2", "a3"};
List<String> idList = Arrays.asList(ids);
```

と書いた時に生成される `idList` が後者の `java.util.Arrays$ArrayList<E>` ということになります。

# new ArrayList<>(Arrays.asList("a1", "a2", "a3")) について考える

というわけで、「なんで `Arrays.asList()` で生成した `ArrayList` は `add` ができないのに、それを `ArrayList` のコンストラクタの引数に渡すだけで `add` できるようになるのか」という疑問の答えはただ一言「違うクラスだから」です。同じクラスだけど生成のされ方によって `add()` の挙動が変わる、というわけではなくて、そもそも別クラスで実装も別なわけですね。

では、ここからは２つのクラスの `add()` メソッドがどのような実装になっているか、また、 `new ArrayList<>(Arrays.asList(ids))` と書いたときにどのような処理がされているかについて少し詳しく見ていきます。

## `java.util.ArrayList<E>` の `add()` メソッド

`java.util.ArrayList<E>` の `add()` メソッドはこんな感じです。

```java:ArrayList.java
transient Object[] elementData; // non-private to simplify nested class access

...省略

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

...省略

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

...省略

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

...省略

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

```

ここまで引用しておいて雑な説明になりますが、要はインスタンスが保持している配列の空き容量をチェックして、空きがあればそのままその配列に、空きがなければ新しく生成した長さが十分確保された配列に、引数で渡したインスタンスを格納する、という流れになっています。

これなら、普通に考えた場合の「新しく追加するインスタンスをリストの最後に追加する」というイメージままの実装なのではないかと思います。

## `java.util.Arrays$ArrayList<E>` の `add()` メソッド

一方　`java.util.Arrays$ArrayList<E>`　ですが、こちらは特に `add()` メソッドについての記述はありません。

ではどこに定義があるのかというと、親クラスの `AbstractList<E>` に実装があります。

```java:Arrays.java
public boolean add(E e) {
    add(size(), e);
    return true;
}

...省略

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}
```

上記の通り、 `AbstractList<E>` は単純に `UnsupportedOperationException` がスローされるようになっており、「`add()` をサポートしたい子クラスが必ず実装してね」ということになっているようです。

最初に `Arrays.asList()` で生成した `ArrayList` は `add()` を呼ぶと `UnsupportedOperationException` がスローされる、と書きましたが、まさにここで実装されている処理が実行されているわけですね。

これが、「`Arrays.asList()` で生成した `ArrayList` は `add()` が使えない」の正体です。

## `new ArrayList<>(Arrays.asList(ids))` と書いたときの処理について

最後に `new ArrayList<>(Arrays.asList(ids))` という処理についてです。

`java.util.ArrayList` のコンストラクタには、以下のように `Collection<? extends E>` を引数に取るものがあります。

```java:ArrayList.java
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
```

`java.util.Collection` は `List` や `Set`、 `Queue` など、すべてのコレクションクラスのインターフェイスです。

`add()` ができない方の `java.util.Arrays$ArrayList` も上述の通り （`Collection` インターフェイスを実装している） `AbstractList` の子クラスですので、このコンストラクタの引数として渡すことができます。

この中で、渡された `Collection` を自分の配列にコピーしていますので、 `new ArrayList<>(Arrays.asList(ids))` と書けば、最初の `ids` 配列の要素を持った、 `add()` が使える `java.util.ArrayList` のインスタンスを生成することができる、という理屈です。

---

結局、今まで書いていた通り

```java
String ids[] = new String[]{"a1", "a2", "a3"};
List<String> idList = new ArrayList<>(Arrays.asList(ids));
```

と書けば良い、という結論なのですが、実際のソースを追いながらどんな理屈になっているのかを探ることでもやもやが一つスッキリし、Java SEの実装を読む練習にもなり、という話でした。

ちなみに、Java SEのソースコードをまだ読んだことがない、という方へ、Java SEのソースコードはJDKの中に含まれる `src.zip` ファイルを解凍することで読むことができます。ご参考まで。