Java Arrays.asListが引き起こす3つの典型的な問題

問題1:プリミティブ型配列をリストに変換する際の落とし穴

Arrays.asList()メソッドはジェネリックメソッドであり、その引数はオブジェクトの配列(T[])を期待しています。しかし、Javaのプリミティブ型(int, doubleなど)はオブジェクトではありません。そのため、プリミティブ型の配列をこのメソッドに渡すと、配列全体が単一の要素として扱われてしまいます。

public class PrimitiveArrayConversion {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        
        // asListは int[] を単一の要素 T として解釈する
        List list = Arrays.asList(numbers);
        
        System.out.println("リストのサイズ: " + list.size());
        System.out.println("リストの内容: " + list);
        System.out.println("リストの最初の要素の型: " + list.get(0).getClass().getName());
    }
}

このコードの実行結果は次のようになります。

リストのサイズ: 1
リストの内容: [[I@1b6d3586]
リストの最初の要素の型: [I

期待されるサイズは3ですが、実際には1になります。これは、int[]配列全体がリストの唯一の要素となっているためです。この問題を回避するには、プリミティブ型の配列をラッパークラスの配列(例:Integer[])に変換してから渡す必要があります。

問題2:返されるリストは構造変更ができない

Arrays.asList()が返すリストは、元の配列を参照する「ビュー」のようなものです。このリストの実体はjava.util.ArrayListクラスではなく、java.util.Arraysの内部プライベートクラスArrayListです。このクラスはAbstractListを継承していますが、要素の追加(add)や削除(remove)といったリストのサイズを変更する操作をサポートしていません。

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

public class UnsupportedOperations {
    public static void main(String[] args) {
        String[] fruits = {"apple", "orange", "banana"};
        List<String> fruitList = Arrays.asList(fruits);
        
        try {
            fruitList.add("grape");
        } catch (UnsupportedOperationException e) {
            System.out.println("要素の追加に失敗しました: " + e.getMessage());
        }
        
        try {
            fruitList.remove(0);
        } catch (UnsupportedOperationException e) {
            System.out.println("要素の削除に失敗しました: " + e.getMessage());
        }
    }
}

このコードを実行すると、UnsupportedOperationExceptionがスローされます。

要素の追加に失敗しました: null
要素の削除に失敗しました: null

リストの要素を更新する(setメソッド)ことは可能ですが、追加や削除を行いたい場合は、新しくArrayListのインスタンスを生成する必要があります。

List<String> mutableList = new ArrayList<>(Arrays.asList(fruits));
mutableList.add("grape"); // これは成功する

問題3:元の配列への変更がリストに反映される

前述の通り、Arrays.asList()が生成するリストは、元の配列を直接参照しています。これは、リストが配列の単なる「ビュー」であることを意味します。そのため、元の配列の要素が変更されると、その変更が即座にリストにも反映されます。

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

public class SharedReference {
    public static void main(String[] args) {
        String[] languages = {"Java", "Python", "C++"};
        List<String> languageList = Arrays.asList(languages);
        
        System.out.println("変更前のリストの1番目の要素: " + languageList.get(1));
        
        // 元の配列を変更する
        languages[1] = "Go";
        
        System.out.println("変更後のリストの1番目の要素: " + languageList.get(1));
    }
}

このコードの実行結果は以下の通りです。

変更前のリストの1番目の要素: Python
変更後のリストの1番目の要素: Go

この動作は、リストと配列がメモリ上で同じデータを共有しているために起こります。この挙動を意図しない場合、予期せぬバグの原因となる可能性があります。配列とは独立したリストが必要な場合は、コンストラクタを使って新しいリストを作成することが推奨されます。

List<String> independentList = new ArrayList<>(Arrays.asList(languages));

タグ: Java Arrays.asList List 配列

6月1日 18:33 投稿