关于 Arrays.asList add、remove 操作的 UnsupportedOperationException

以前在项目中遇到过这么一个问题,就是用Arrays.asList()将一个数组转换成list后,执行一些 add 、remove等操作时总是报UnsupportedOperationException错误,今天在看源码时才发现,原来Arrays.asList()这个方法转换后的list实现类不是我们常用的ArrayList,而是
一个Arrays的静态内部类,该类直接继承AbstractList类:

 /**
     * @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;
        }
    }

注意,此类没有重写父类AbstractList的 add(E e)、remove(int index)方法,而父类的这两个方法体没有任何有意义的操作,只是抛了一个UnsupportedOperationException异常:

/**
     * Appends the specified element to the end of this list (optional
     * operation).
     *
     * <p>Lists that support this operation may place limitations on what
     * elements may be added to this list.  In particular, some
     * lists will refuse to add null elements, and others will impose
     * restrictions on the type of elements that may be added.  List
     * classes should clearly specify in their documentation any restrictions
     * on what elements may be added.
     *
     * <p>This implementation calls {@code add(size(), e)}.
     *
     * <p>Note that this implementation throws an
     * {@code UnsupportedOperationException} unless
     * {@link #add(int, Object) add(int, E)} is overridden.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     * @throws UnsupportedOperationException if the {@code add} operation
     *         is not supported by this list
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this list
     * @throws NullPointerException if the specified element is null and this
     *         list does not permit null elements
     * @throws IllegalArgumentException if some property of this element
     *         prevents it from being added to this list
     */
    public boolean add(E e) {
        add(size(), e); //调用add(int index, E element)方法,此方法抛异常
        return true;
    }

 /**
     * {@inheritDoc}
     *
     * <p>This implementation always throws an
     * {@code UnsupportedOperationException}.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws IndexOutOfBoundsException     {@inheritDoc}
     */
    public E remove(int index) {
        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();
    }

那为啥会这样呢,重写一下很麻烦吗?按理说,jdk开发者绝对不会这么粗心大意的,这样做毕竟有其道理,以下是个人猜想,估计八九不离十。

开发Arrays.asList这个方法,是将数组转换成list,以便Java开发者能用更习惯的方式来操作这些元素,而这个方法的意义,也仅限于操作这个数组本身,也就是转换后的list本质上还是对这个数组的引用,一切操作的都会直接作用于数组本身:

        String[] strArr = {"aaa","ccc","bbb"};

        List<String> li = Arrays.asList(strArr);
        Collections.reverse(li);

        //输出:bbb  ccc  aaa  
        for(String str : strArr){
            System.out.print(str+" ");
        }
        //li.set(1, "lll");

可以看到,我们对li进行反转操作的结果是strArr的元素同样也反转了

而如果重写add、remove会有啥后果?要知道,数组是长度固定的,add、remove等操作如果合法,要么原来数组的长度改变,这与数组长度固定矛盾,要么将list的数组引用到一个新的数组,这与Arrays.asList()设计初衷矛盾————故不能让其成为一个合法的操作。

最后留一个小问题,上面测试代码中的li.set(1, “ccc”)方法能正常执行吗,为什么?

最后附个小贴士:
如果想让Arrays.asList()生成的List与初始数组脱离关系,成为一个普通的java.util.ArrayList可以吗?当然可以!java.util.ArrayList类有一个重载的构造方法public ArrayList(Collection<? extends E> c)
此方法接收Collection实现类,把接收对象的数据拷贝一份作为自己的数据,而 Arrays.asList()生成的List当然也是Collection的实现类,所以可以直接入参:

List<String> li = Arrays.asList(strArr);
ArrayList<String> mylist = new ArrayList<String>(li);

猜你喜欢

转载自blog.csdn.net/u011193276/article/details/50091733