Arrays.asList()的坑,踩过再也不会踩系列

本文详细探讨了Java中Arrays.asList()方法在转换数组到列表时的浅拷贝特性,强调基本类型数组和引用类型数组的区别。通过源码分析,揭示了asList创建的列表无法直接进行add和remove操作的原因,并给出了正确的转换和使用方法。同时,文章指出在需要修改列表内容时,需要将asList转换为ArrayList实例,以避免对原数组的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Arrays.asList的大坑以及正确使用方法

1.1 先抛出关于asList的使用的结论

  • 基本数据类型的数组不要试图使用该方式转换为list,老老实实用for循环一个个加入list中去;引用数据类型的数组(包括二维数组)都可以采用该方式转换为list

    //正确的转换方式
    Integer[] arr = new Integer[n];
    for(...)...//赋值
    List<Integer> list = new ArrayList<>(Arrays.asList(arr));
    
  • 如果只用于遍历,使用Arrays.asList(arr)即可;但是如果需要对list集合进行remover和add操作,就需要采用上述操作转换为util包下的ArrayList的方式了——所以,咱们倒不如每次都转换?害,看大家个人选择吧!

  • arrayList底层就是数组存储,而asList方式就是完成了浅拷贝的操作,因此使用set(int index, Object element)操作修改集合之后或者

1.2 源码分析

1.2.1 asList的浅拷贝操作分析

public static <T> List<T> asList(T... a) {
    return new Arrays.ArrayList(a);//这里也可以看到,直接new的是Arrays的内部静态类:java.util.Arrays.ArrayList
}
//看到该有参构造器,直接将带转换的数组的引用传给Objects工具类的另外一个方法,该方法中将该ArrayList的数组a的引用赋值为待转换的数组,相当于返回的ArrayList和原数组指向的是同一份数据——浅拷贝
ArrayList(E[] array) {
    this.a = (Object[])Objects.requireNonNull(array);
}
public static <T> T requireNonNull(T obj) {
    if (obj == null) {
        throw new NullPointerException();
    } else {
        return obj;
    }
}

简单测试如下:

List<Integer> list = Arrays.asList(arr);

System.out.println("列表原内容为:");
for(int i : list)
    System.out.print(i + " ");
System.out.println();
System.out.println("******************");

System.out.println("数组原内容为:");
for(int i : arr)
    System.out.print(i + " ");
System.out.println();
System.out.println("******************");

list.set(1, 10);//更新列表内容:虽然没有add和remove方法,但是存在set方法可以按照位序修改元素

System.out.println("列表内容更新为:");
for(int i : list)
    System.out.print(i + " ");
System.out.println();
System.out.println("******************");

System.out.println("数组遍历结果更新为:");
for(int i : arr)
    System.out.print(i + " ");
System.out.println();
列表原内容为:
0 1 2 3 4 
******************
数组原内容为:
0 1 2 3 4 
******************
列表内容更新为:
0 10 2 3 4 
******************
数组遍历结果更新为:
0 10 2 3 4 
******************

通过上面简单的测试我们发现:是浅拷贝而已,修改列表内容原数组内容也会跟着修改。

1.2.2 新生成的List实例并非java.util.ArrayList的实例

在这里插入图片描述

在这里插入图片描述

上图可以明显看到,asList生成的ArrayList仅仅是Arrays的一个内部静态类的实例java.util.Array.ArrayList!而非java.util.ArrayList!

且该ArrayList不存在add和remove方法,如果进行增删的话会报错!

### Java `Arrays.asList` 常见问题和注意事项 #### 方法签名与返回类型 `Arrays.asList` 是一个便捷的方法,用于创建固定大小的列表。该方法接受可变数量的参数并将其转换为列表对象[^1]。 ```java List<String> list = Arrays.asList("a", "b", "c"); ``` 需要注意的是,此方法返回的对象是一个由数组支持的固定大小的列表实例。因此,尝试修改列表长度的操作将会抛出异常。 #### 修改操作受限 由于返回的列表是基于原始数组构建而来,任何试图改变其结构(即增加或删除元素)的行为都会引发 `UnsupportedOperationException` 异常。然而,可以替换现有索引位置上的元素: ```java list.set(0, "d"); // 正确 // 下面这行会报错 // list.add("e"); // 抛出 UnsupportedOperationException ``` 对于希望获得完全动态特性的场景,则应考虑使用新的 `ArrayList` 构造器来初始化一个新的列表副本: ```java List<String> newList = new ArrayList<>(Arrays.asList("a", "b", "c")); newList.add("d"); // 成功执行 ``` #### 泛型处理不当的风险 当传递给 `asList()` 的参数不是基本数据类型的封装类时,在某些情况下可能会遇到意外的结果。特别是如果传入单个数组作为参数,那么整个数组会被视为单一元素而非多个独立项组成的序列: ```java String[] array = {"a", "b", "c"}; List<String[]> wrongUsage = Arrays.asList(array); // 列表只有一个元素,即原数组本身 for (Object obj : wrongUsage) { System.out.println(obj.getClass()); // 输出 class [Ljava.lang.String; } ``` 为了得到期望中的字符串列表而不是包含数组本身的列表,应该直接指定泛型参数或将数组展开成离散参数: ```java List<String> correctUsage = Arrays.asList("a", "b", "c"); // 或者通过 Stream API 转换 List<String> streamConversion = Arrays.stream(array).toList(); ``` #### 多线程环境下的行为 考虑到 `Arrays.asList` 返回的列表并非同步实现,所以在多线程环境中共享此类列表之前应当采取额外措施确保线程安全。可以通过 Collections.synchronizedList() 来获取同步版本的列表[^2]。 ```java List<String> threadSafeList = Collections.synchronizedList(Arrays.asList("a", "b", "c")); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值