はじめに
久々にJavaを使った時にArrays.asList()で少々嵌ったので、Arrays.asList()の注意点について備忘録として書き残しておきます。
Arrays.asList()とは
- 配列を操作するためのユーティリティクラスであるArraysクラスで定義されているメソッド。
- ざっくり言うと「引数で指定した配列をリストとして返す」というメソッド。
String[] words = {"abc","def","ghi"};
List<String> list = Arrays.asList(words);
Arrays.asList()の注意点
1. 戻り値のリストの長さが固定
指定された配列に連動する固定サイズのリストを返します。
JavaのAPIには、上記のように書かれています。
つまり、asListメソッドの戻り値がリストであっても、リストの長さが可変ではない(=追加や削除ができない)ということです。
例えば、asListメソッドの戻り値に対してaddメソッドやremoveメソッドで要素数を変更しようとすると、コンパイルエラーは発生しませんが、実行時にjava.lang.UnsupportedOperationExceptionが発生します。
この点についてはAPIにも書かれているので、きちんとAPIを確認しておけば問題なくクリアできると思います。
// 固定長のリストに対する要素の追加
static void add() {
List<Integer> fixedSizeList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 実行時にUnsupportedOperationExceptionが発生する。
fixedSizeList.add(11);
}
// 固定長のリストに対する要素の削除
static void remove() {
List<Integer> fixedSizeList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 実行時にUnsupportedOperationExceptionが発生する。
fixedSizeList.remove(5);
}
「リストの長さが固定されている」という問題については、以下のように「asListメソッドで生成したリストを、ArrayListのコンストラクタに指定する」とすることで回避できます。
リストから配列への変換はList#toArray()を使えば一発で変換できますが、配列から可変長のリストへの変換はどうしても2段階になってしまうようです。
// Arrays.asList()で作成したリストを、可変長(ミュータブル)のリストに変換する。
static void add2() {
List<Integer> fixedSizeList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
List<Integer> list = new ArrayList<>(fixedSizeList);
// 実行時にエラーが発生しない。
list.add(11);
}
2. 引数にプリミティブ型の配列を指定すると想定外の挙動をする
Arrays.asList()の引数にプリミティブ型の配列を指定すると、戻り値がList<ラッパークラス型>
とはなりません。
例えば、Arrays.asList()の引数にint[]を指定すると、戻り値がList<int[]>
となってしまいます。
これはAPIには一切書かれていなかったので、最初はコンパイルが通らずに悩みましたが、結果的には「仕様通りの動き」ということが分かって安心しました。
この問題を回避するには、「ラッパークラス型の配列を引数として指定する」などの方法があるそうですが、わざわざラッパークラス型に変換しておくのも変な感じがします。
static void create1() {
// 参照型の配列だと、参照型のListが想定通りに返されますが...
String[] words = {"abc","def","ghi"};
List<String> list1 = Arrays.asList(words);
}
static void create2() {
// プリミティブ型の配列をasList()の引数にすると、「配列がリスト中の1要素となる」ため、要素数1のリストが返されます。
// list2.size()は1となります。
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
List<int[]> list2 = Arrays.asList(numbers);
System.out.println("list2の長さ:" + list2.size());
}
static void create3() {
// 可変長の引数をasList()に渡すと、引数が整数値(=int)であっても、想定通りにリストが生成されます。
// list3.size()は10となります。
List<Integer> list3 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("list3の長さ:" + list3.size());
}
まとめ
- Arrays.asList()で得られるリストは、固定長のリストとなる。
- Arrays.asList()の引数にプリミティブ型の配列を指定すると、配列の要素が展開されず、配列そのものが戻り値のリストの1要素となる。
- 特にリストに変換する必要が無ければ、そのままプリミティブ型の配列として使った方が良いかもしれません...