Arrays.asList注意事项

Posted by Yezhiwei on July 2, 2020

Array 转 ArrayList

当需要把 Array 转成 ArrayList 的时候,经常这样做

String[] arr = new String[]{"aa","bb"};
List<String> list = Arrays.asList(arr);

Arrays.asList() 会返回一个 ArrayList,但是要特别注意,这个 ArrayListArrays 类的静态内部类,并不是 java.util.ArrayList 类。

asList 返回一个由指定数组生成的固定大小的 List

List 是可以动态扩容的,因此在创建一个 List 之后最常见的操作就是向其中添加新的元素或是从里面删除已有元素

public static void main(String[] args) {
    String[] arr = new String[]{"aa","bb"};
    List<String> list = Arrays.asList(arr);
    list.add("Guava");
}    

尝试运行这段代码,结果抛出了一个 java.lang.UnsupportedOperationException 异常。这一异常意味着,向 list 添加新元素是不被允许的,如果试图从 list 中删除元素,也会抛出相同的异常。

原因是 java.util.Arrays.ArrayList 类实现了 set()get()contains()方法,但是并没有实现增加元素的方法,通过查看源码可以发现调用 add 方法,没有具体实现,仅仅抛出 UnsupportedOperationException 异常),因此它的大小也是固定不变的。

创建一个真正的 ArrayList

为了创建一个真正的 java.util.ArrayList,你应该这样做

String[] arr = new String[]{"aa","bb"};
List<String> list = new ArrayList<String>(Arrays.asList(arr));

ArrayList 的构造方法可以接收一个 Collection 类型,而 java.util.Arrays.ArrayList 已经实现了该接口。

不能直接使用 Arrays.asList 来转换基本类型数组

初始化三个数字的 int[] 数组,然后使用 Arrays.asList 把数组转换为 List,代码如下

int[] arr = {1, 2, 3};
List list = Arrays.asList(arr);
log.info("list:{} size:{} class:{}", list, list.size(), list.get(0).getClass());

这样初始化的 List 并不是我们期望的包含 3 个数字的 List。通过日志可以发现,这个 List 包含的其实是一个 int 数组,整个 List 的元素个数是 1,元素类型是整数数组。

list:[[I@1c53fd30] size:1 class:class [I

来看一下 asList 方法的签名

public static <T> List<T> asList(T... a)

asList 方法的参数必须是对象或者对象数组,而原生数据类型不是对象,当传入一个原生数据类型数组时,asList 的真正得到的参数就不是数组中的元素,而是数组对象本身。

数据类型的数组作为参数正确的方式

如果使用 Java8 以上版本可以使用 Arrays.stream 方法来转换,否则可以把 int 数组声明为包装类型 Integer 数组

int[] arr1 = {1, 2, 3};
List list1 = Arrays.stream(arr1).boxed().collect(Collectors.toList());
log.info("list:{} size:{} class:{}", list1, list1.size(), list1.get(0).getClass());


Integer[] arr2 = {1, 2, 3};
List list2 = Arrays.asList(arr2);
log.info("list:{} size:{} class:{}", list2, list2.size(), list2.get(0).getClass());

对原始数组的修改会影响到我们获得的那个 List

String[] arr = {"1", "2", "3"};
List list = Arrays.asList(arr);
arr[1] = "4";
log.info("arr:{} list:{}", Arrays.toString(arr), list);
arr:[1, 4, 3] list:[1, 4, 3]

从日志中可以看出,把原始数组的第二个元素从 2 修改为 4 后,asList 获得的 List 中的第二个元素也被修改为 4 了。

看一下 Arrays 内部类 ArrayList 的实现,可以发现 ArrayList 其实是直接使用了原始的数组。所以,我们要特别小心,把通过 Arrays.asList 获得的 List 交给其他方法处理,很容易因为共享了数组,相互修改产生 Bug。

修复方式比较简单,重新 new 一个 ArrayList 初始化 Arrays.asList 返回的 List 即可

String[] arr = {"1", "2", "3"};
List list = new ArrayList(Arrays.asList(arr));
arr[1] = "4";
log.info("arr:{} list:{}", Arrays.toString(arr), list);
arr:[1, 4, 3] list:[1, 2, 3]

强制类型转换

String[] arr = {"1", "2", "3"};
ArrayList<String> list = (ArrayList<String>)Arrays.asList(arr);
java.lang.ClassCastException: java.util.Arrays$ArrayList cannot be cast to java.util.ArrayList

关于 Arrays 使用过程中的坑就说到这里,欢迎留言补充。

如果觉得还有帮助的话,你的关注和转发是对我最大的支持,O(∩_∩)O: