正确认识Arrays.asList方法

Posted GarfieldEr007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正确认识Arrays.asList方法相关的知识,希望对你有一定的参考价值。

Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List 

其源代码如下:

    /**
     * Returns a fixed-size list backed by the specified array.  (Changes to
     * the returned list "write through" to the array.)  This method acts
     * as bridge between array-based and collection-based APIs, in
     * combination with @link Collection#toArray.  The returned list is
     * serializable and implements @link RandomAccess.
     *
     * <p>This method also provides a convenient way to create a fixed-size
     * list initialized to contain several elements:
     * <pre>
     *     List&lt;String&gt; stooges = Arrays.asList("Larry", "Moe", "Curly");
     * </pre>
     *
     * @param a the array by which the list will be backed
     * @return a list view of the specified array
     */
    @SafeVarargs
    public static <T> List<T> asList(T... a) 
        return new ArrayList<>(a);
    

 

问题发现

根据上述方法的描述,我们先来编写几个例子:

/**
 * @author wangmengjun
 *
 */
public class ArrayExample 
    
    public static void main(String[] args) 
        
        /**使用变长参数*/
        List<String> array1 = Arrays.asList("Welcome", "to","Java", "world");
        System.out.println(array1);
        
        /**使用数组*/
        List<String> array2 =  Arrays.asList(new String[] "Welcome", "to","Java", "world");
        System.out.println(array2);
    

 

运行上述程序,输出如下内容。

[Welcome, to, Java, world]
[Welcome, to, Java, world]

 

心血来潮,突然想在创建的列表中添加一个字符串“Cool~~~”,  走一个。

        /**使用变长参数*/
        List<String> array1 = Arrays.asList("Welcome", "to","Java", "world");
        array1.add("Cool~~~");

结果,遇到一个UnsupportedOperationException异常:

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(Unknown Source)
	at java.util.AbstractList.add(Unknown Source)
	at test.ArrayExample.main(ArrayExample.java:36)

不可思议,new ArrayList<>(a)产生的列表调用add方法,竟然遇到问题。

原因查找

那么问题来了,到底发生了什么事情?带着疑问,去查看一下Arrays.asList中使用的ArrayList到底长啥样?

原来Arrays的asList方法使用的ArrayList类是一个内部定义的类而不是java.util.ArrayList类

其源代码如下:


    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) 
            if (array==null)
                throw new NullPointerException();
            a = array;
        

        public int size() 
            return a.length;
        

        public Object[] toArray() 
            return a.clone();
        

        public <T> T[] toArray(T[] a) 
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        

        public E get(int index) 
            return a[index];
        

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

        public int indexOf(Object o) 
            if (o==null) 
                for (int i=0; i<a.length; i++)
                    if (a[i]==null)
                        return i;
             else 
                for (int i=0; i<a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            
            return -1;
        

        public boolean contains(Object o) 
            return indexOf(o) != -1;
        
    

 

从这个内部类ArrayList的实现可以看出,它继承了抽象类java.util.AbstractList<E>, 但是没有重写addremove方法,没有给出具体的实现

但是,默认情况下,java.util.AbstractList类在addset以及remove方法中,直接会抛出UnsupportedOperationException异常。AbstractList的部分源代码如下:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> 
    /**
     * Sole constructor.  (For invocation by subclass constructors, typically
     * implicit.)
     */
    protected AbstractList() 
    

    public E set(int index, E element) 
        throw new UnsupportedOperationException();
    

    /**
     * @inheritDoc
     *
     * <p>This implementation always throws an
     * @code UnsupportedOperationException.
     *
     * @throws UnsupportedOperationException @inheritDoc
     * @throws ClassCastException            @inheritDoc
     * @throws NullPointerException          @inheritDoc
     * @throws IllegalArgumentException      @inheritDoc
     * @throws IndexOutOfBoundsException     @inheritDoc
     */
    public void add(int index, E element) 
        throw new UnsupportedOperationException();
    

    /**
     * @inheritDoc
     *
     * <p>This implementation always throws an
     * @code UnsupportedOperationException.
     *
     * @throws UnsupportedOperationException @inheritDoc
     * @throws IndexOutOfBoundsException     @inheritDoc
     */
    public E remove(int index) 
        throw new UnsupportedOperationException();
    

 

正是因为java.util.Arrays类的内部类ArrayList没有重写add和remove方法,所以,当我们调用其add方法时,其实就是调用了AbstractList类的add方法,结果就是直接抛出UnsupportedOperationException异常。

同理,在调用remove方法,或者调用与add、remove方法相关联的其它方法(如addAll)同样会遇到UnsupportedOperationException异常。

addAll的例子

/**
 * @author wangmengjun
 *
 */
public class ArrayExample 

    public static void main(String[] args) 

        /**使用变长参数*/
        List<String> array1 = Arrays.asList("Welcome", "to", "Java", "world");
        array1.addAll(Arrays.asList("AAA", "BBB"));
    

 

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(Unknown Source)
	at java.util.AbstractList.add(Unknown Source)
	at java.util.AbstractCollection.addAll(Unknown Source)
	at test.ArrayExample.main(ArrayExample.java:36)

 

set的例子:

/**
 * @author wangmengjun
 *
 */
public class ArrayExample 

    public static void main(String[] args) 

        /**使用变长参数*/
        List<String> array1 = Arrays.asList("Welcome", "to", "Java", "world");
        System.out.println(array1);
        
        //将Java替换成hello
        array1.set(2, "hello");
        System.out.println(array1);
    

正是由于Arrays的内部类ArrayList重写了set方法,所以上述程序能够正常运行,不会再抛出UnsupportedOperationException异常。

结果如下:

[Welcome, to, Java, world]
[Welcome, to, hello, world]

 

使用场景

从上述的例子和简单分析来看,Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List 

但是,生成的List的长度是固定的;能够进行修改操作(比如,修改某个位置的元素)不能执行影响长度的操作(如add、remove等操作)。会抛出UnsupportedOperationException异常

Arrays.asList比较适合那些已经有数组数据或者一些元素,而需要快速构建一个List,只用于读取操作,而不进行添加或删除操作的场景。

如果,想要根据已知数组数据,快速获取一个可进行增删改查的列表List,一个比较简单的方法如下:

重新使用java.util.ArrayList包装一层。

/**
 * @author wangmengjun
 *
 */
public class ArrayExample 

    public static void main(String[] args) 

        /**使用变长参数*/
        List<String> array1 = new ArrayList<>(Arrays.asList("Welcome", "to", "Java", "world"));
        System.out.println(array1);

        array1.add("Cool~~~");
        System.out.println(array1);

    

结果如下:

[Welcome, to, Java, world][Welcome, to, Java, world, Cool~~~]

from: https://my.oschina.net/wangmengjun/blog/760998

以上是关于正确认识Arrays.asList方法的主要内容,如果未能解决你的问题,请参考以下文章

Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)

Arrays.asList()用法梳理

Java如何正确的将数值转化为ArrayList?

Java-Arrays类-Arrays.asList()方法详解

Arrays.asList方法遇到的问题

Arrays.asList的用法