2013年2月9日土曜日

[Java][Android]Arrays.asListについて

Javaには
Arrays.asList()という配列をリストに変換するメソッドがあります。

仕様上の問題で、配列ではなくリストで渡さないといけないというのが
たまーにあるので重要という程でもないですが、知っていると便利なメソッドです。



こういうコードを書いてみました。

これを実行すると、asList.remove(0)で例外が発生します。



実は、Arrays.asList()で取得したリストは不変(addもremoveもできない)という仕様があります。
(ドキュメントにもちゃんと書いてあります)

たまに忘れます...


ではこのArrays.asList()で取得したリストとは何なのでしょうか。



少し調査してみました。(Java6を対象にしています)


Eclipse でasListの部分をクリックして 宣言を開く (F3) をしてみます。
ArraysクラスのasList の宣言が見れます。


そうするとあれ??
ArrayListに引数のものを入れて返してるだけじゃないですか。

つまりasList()で取得したものはArrayListということになり、可変(addもremove)も出来るはず。


しかしここに落とし穴があり。

 
さらにこのArrayListのところで F3を押して ArrayListの宣言を見てみます。





(一部分のみ)


気づきにくいですが、これは Arraysクラスの中の内部クラスとして宣言されているものなのです。

我々がよく使うArrayListとは別物なのです。

ちなみにadd メソッドは ここでは宣言されてなくて extends のAbstractListを辿って
add(E element)を辿り

add(int index, E element) 
の宣言部分

を見るとこのようになっております。
中身が例外だけ・・・・。(゚д゚)!

とりあえずこの仕組みによって例外が投げられているのですね。



ちなみに、通常のArrayListを調べると 同じくextends AbstractList されていますが、

きちんとオーバーライドでaddの中身が実装されています。


ちなみに
Arraysの中のArrayList(T[] array)という宣言は 通常のArrayListのコンストラクタにはありません。


なので配列の要素を全部渡すときは これがエレガントな方法です。


(2013/11/30 追加  yozaさんthx)




今回主にJavaに関してのことでしたが Androidアプリでも同様です
(そもそもきっかけはこちら)

ListViewの表示でよく使う ArrayAdapterで なぜかremove()をするとなぜか失敗していたので調べた結果たどり着いたのが

ArrayAdapterのコンストラクタのこの部分。
Arrays.asList()が使われてます。


つまりこれで作成されたArrayAdapterの内部で使われるリストは不変になってしまいます。

JavaDocの説明に これで渡すと不変であると書いてないじゃないですか。。。orz  
(テストしてなかったこちらも悪いですけど)

まとめ

Arrays.asList で取得したリストは不変です。
宣言の中身を見ると、同名のArrayListが使われてます。(ややこしい)

不変の理由は、addやremoveの中身が無い実装のAbstractListのため

AndroidのArrayAdapterのコンストラクタで配列を渡すと不変リストになってしまうので注意です。重要