Arrays.asList使用注意事项

项目中经常使用Arrays.asList方法方便的得到的list,发现有很多细节的知识点可以总结一下,总结的注意事项有以下几点:

1)通过Arrays.asList方法得到的list是固定大小的,不可以add或remove方法去改变list的元素

2)不支持基础数据类型的转换

3)改变原始数组的内容或者改变通过Arrays.asList方法得到的list的元素值值,相互之间的数据都会自动更新,因为这个方法是将数组和列表链接起来

下面来通过测试解释一下原因:

1)通过Arrays.asList方法得到的list是固定大小的,不可以add或remove方法去改变list的元素

 看源码我们可以看到Arrays.asList是通过Arrays的一个内部类ArrayList实现的,这个内部类继承自AbstractList同时实现了RandomAccess和Serializable接口,所以这里得到的

ArrayList 并不是 java.util.ArrayList ,而是 java.util.Arrays 的一个内部类,这个内部类用一个 final 数组来保存元素。
private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable

继承的AbstractList中对add、remove、set方法是直接抛异常的,也就是说如果继承的子类没有去重写这些方法,那么子类的实例去调用这些方法是会直接抛异常的

下面是AbstractList中方法的定义,我们可以看到具体抛出的异常:

public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }

我们来验证一下结论是不是这样的,转换之后我们调用一下add方法:

public class ArraysListTest {

    public static void main(String[] args) {
        Integer[] a_Integer= new Integer[] { 1, 2, 3, 4 };
        List a_Integer_List = Arrays.asList(a_Integer);
        a_Integer_List.add(5);

    }
}

看下运行的结果:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)

抛出来异常和AbstractList中add方法定义抛出来的异常是一样,也验证了是不可操作的

那我们再验证一下set方法,再获取到list后,通过set方法去改变第一个元素的值,测试用例如下:

public class ArraysListTest {

    public static void main(String[] args) {
        Integer[] a_Integer= new Integer[] { 1, 2, 3, 4 };
        List a_Integer_List = Arrays.asList(a_Integer);
        a_Integer_List.set(0,888);
        foreach(a_Integer_List);
    }
    /*打印方法*/
    private static void foreach(List list) {
        for (Object object : list) {
            System.out.println(object + " ");
        }

        System.out.println();

    }
}

看戏运行结果:

888 
2 
3 
4 

不仅没有抛异常,还能做到改变第一个元素的值,这是为什么呢?结论是Arrays内部定义的内部类ArrayList重写了set方法,使得set功能是可以操作的

  @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

2)不支持基础数据类型的转换

 Java中的基础数据类型(byte,short,int,long,float,double,boolean)是不支持使用Arrays.asList方法去转换的

首先看下Arrays.asList方法的定义:

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

参数类型是一个T,根据官方文档的描述,T 是数组元素的 class

我们知道任何类型的对象都有一个 class 属性,这个属性代表了这个类型本身。我们常用的反射技术,也是通过class来获取到一个对象,原生数据类型,比如 int,short,long等,是没有这个属性的,具有 class 属性的是它们所对应的包装类 Integer,Short,Long,可以理解为asList 方法的参数必须是对象或者对象数组,而基础数据类型不是对象——这也正是包装类出现的一个主要原因。当传入一个基础数据类型数组时,asList 的真正得到的参数就不是数组中的元素,而是 数组对象本身,此时List 的唯一元素就是这个数组。
 
我们定义一个基础类型的数组,使用asList转换后得到的结果:
public class ArraysListTest {

    public static void main(String[] args) {
        int [] a_int = { 1, 2, 3, 4 };
        /*预期输出应该是在1,2,3,4,但实际上输出的仅仅是一个引用,这里它把a_int 当成一个元素*/
        List a_int_List = Arrays.asList(a_int);
        foreach(a_int_List);
        /*为此我们需要这样遍历其中元素*/
        foreachForBase(a_int_List);
    }
    /*打印方法*/
    private static void foreach(List list) {
        for (Object object : list) {
            System.out.println(object + " ");
        }
        System.out.println();
    }
    private static void foreachForBase(List a_int_List) {
        int[] _a_int = (int[]) a_int_List.get(0);
        foreach(_a_int);
    }
    private static void foreach(int[] a_int) {
        for (int i : a_int) {
            System.out.println(i + " ");
        }
        System.out.println();
    }
}
看下运行的结果:
[I@36f6e879 

1 
2 
3 
4 

我们可以看到第一个打印出来的结果是数组对象的hashcode值,并不是真正数组元素。

那么如果我们想对基础数据类型的数组使用asList,就需要先转换为包装类型或者在申明的时候就定义为包装类型。
 

3)改变原始数组的内容或者改变通过Arrays.asList方法得到的list的元素值值,相互之间的数据都会自动更新,因为这个方法是将数组和列表链接起来 

当我们去改变其中的list或者数组的元素时,两个是同时改变的,转后list后,我们改变list中元素的值,再看下数组的值:
public class ArraysListTest {

    public static void main(String[] args) {
        Integer[] a_Integer= new Integer[] { 1, 2, 3, 4 };
        List a_Integer_List = Arrays.asList(a_Integer);
        a_Integer_List.set(0,888);
        foreach(a_Integer_List);
        foreach(a_Integer);
    }
    /*打印方法*/
    private static void foreach(List list) {
        for (Object object : list) {
            System.out.println(object + " ");
        }
        System.out.println();
    }
    private static void foreach(Integer[] _a_Integer) {
        for (int i : _a_Integer) {
            System.out.println(i + " ");
        }
        System.out.println();
    }
}

看下运行的效果:

888 
2 
3 
4 

888 
2 
3 
4 

可以看到数组的值也跟着改变了,发过来改变数组的值,list元素的值也是变更的,因为asList获得List实际引用的就是数组

最后,如果我们想得到一个真正的可操作的list通过什么方法呢?

第一种最简便的方式:new 一个真正的java.util.ArrayList 

        List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3));

第二种使用Java8的stream方式:

Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本类型也可以实现转换(依赖boxed的装箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());
 

猜你喜欢

转载自www.cnblogs.com/wangflower/p/12452355.html