Arrays.asList源码解析及常见误区

Arrays.asList源码解析及常见误区

一、源码解析

首先我们来看看Arrays.asList()的源码


@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}
  1. @SafeVarargs 注解表示该方法使用了可变参数,并且在类型安全的前提下进行了操作。
  2. @SuppressWarnings("varargs") 注解用于抑制关于可变参数的警告。
  3. 方法签名:public static <T> List<T> asList(T... a)
  4. 方法功能:该方法接受一个可变参数 a,并将其转换为一个ArrayList对象,并返回。
  5. return new ArrayList<>(a); 创建一个新的ArrayList对象,并将可变参数 a 作为构造方法的参数传入。这样就将可变参数转换为了ArrayList对象。

​可以看到源码的作用是将一个可变参数转换为ArrayList对象,方便对其进行操作和访问。

​ 这样看来似乎没有暴露什么问题,难道真的那么简单吗?

二、常见问题

1.基本类型数组作为参数问题

先进行如下测试:

  public static void main(String[] args) {

        int[] a = {2,3,4,5};
        Integer[] b = {2,3,4,5};

        List listA = Arrays.asList(a);
        List listA1 = Arrays.asList(2,3,4,5);
        List listB = Arrays.asList(b);

        System.out.println(listA.size());
        System.out.println(listA1.size());
        System.out.println(listB.size());
      
     	System.out.println("ListA元素类型:"+listA.get(0).getClass());
										
		System.out.println("ListA元素:"+Arrays.toString((int[]) listA.get(0)));

		System.out.println("ListA1元素类型:"+listA1.get(0).getClass());

		System.out.println("ListB元素类型:"+listB.get(0).getClass());
    }

结果如下:

在这里插入图片描述

​我们发现用int类型的数组作为参数为什么输出size是1呢,使用Integer类型size就是4了。们知道基本类型是不能泛型化的,就是说8种基本类型不能作为泛型参数,要想作为泛型参数就要使用其所对应的包装类。为什么listA的Size为什么是1呢?这是因为listA传递的是一个int类型的数组,数组是一个对象,它是可以泛型化的,也就是说例子中是把一个int类型的数组作为了T的类型,所以转换后在List中就只有一个类型为int数组的元素。后边ListA1与ListB也就可以理解了,一个是进行了自动打包,一个是本来就是包装类型。

2.asList()方法返回对象使用add()方法抛出异常

先进行如下测试:

  public static void main(String[] args) {
        List<String> pets = Arrays.asList("tiger","mice");
        pets.add("lion");
    }

结果如下:

在这里插入图片描述

我们这里可以看到他抛出了一个异常,难道add方法不是List的基本用法吗?我们继续扒一下源码。

原来原来Arrays的asList方法使用的ArrayList类是一个内部定义的类,而不是java.util.ArrayList类。其部分源码如下:

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        @java.io.Serial
        private static final long serialVersionUID = -2764017481108945198L;
        @SuppressWarnings("serial") // Conditionally serializable
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }
        
        ....
     }

​我们可以看到这是一个静态内部类,存储数组元素的a变量是final类型的,由此判断,这个静态内部类是不能做任何内部元素的添加删除操作的!就跟String类一样,String对象存储字符数组的变量也是有final修饰符的。因为一旦增加数组元素,这个数组容量已经定好的容器就无法装载增加的元素了。

内部类里面并没有add,remove方法,我们可以看下这个类继承的AbstractList类里面对这些方法的实现:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {  
    ........  
  
    public void add(int index, E element) {  
    	throw new UnsupportedOperationException();  
    }  
  
    public E remove(int index) {  
    	throw new UnsupportedOperationException();  
    }  
} 

找到异常的来源了,我们使用asList得到的对象add、remove方法直接就是抛出异常。

那么我们如果要想对asList得到的对象使用add、remove方法,该怎么办呢?

List<String> pets = new ArrayList<String>(Arrays.asList("a", "b", "c")); 

可以将传入的参数转换为一个ArrayList对象返回。

3.通过Arrays.asList方法将数组转成集合后,使用set方法修改元素,为什么原来的数组的值也会改变?

测试:

public static void main(String[] args){
            String[] strings={"A","B","C"};
            List<String> stringList=Arrays.asList(strings);
            stringList.set(0,"G");
            System.out.println(stringList);
            System.out.println(strings[0]);
        }

结果:

​我们可以看到集合的值和原数组的第一个值都被修改了,为什么原数组也会被修改呢?

原因是静态内部类ArrayList的成员变量a使用了final,用于存储集合的数组引用始终被强制指向原有数组。

所以原数组也会被修改。

​那么怎么在不修改原数组的基础上修改集合呢?我们同样可以采取将传入的参数转换为一个ArrayList对象返回的方式。

总结:

​数组通过Arrays.asList()方法转成的集合是固定的,因为其使用的ArrayList类是一个内部定义的类,不是java.util.ArrayList类,不支持add()、remove()、clear()等会修改list集合元素个数的方法。要想使用它可以将转换的集合作为参数给ArrayList类对象。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值