java源码分析 Arrays.asList()与Collections.unmodifiableList()

举个栗子

本章示例代码来自java编程思想——17.4.1未获支持的操作——Unsupported类。

import java.util.*;

public class Unsupported {
    static void test(String msg, List<String> list) {
        System.out.println("--- " + msg + " ---");
        Collection<String> c = list;
        Collection<String> subList = list.subList(1,8);
        // Copy of the sublist:
        Collection<String> c2 = new ArrayList<String>(subList);
        try { c.retainAll(c2); } catch(Exception e) {
            System.out.println("retainAll(): " + e);
        }
        try { c.removeAll(c2); } catch(Exception e) {
            System.out.println("removeAll(): " + e);
        }
        try { c.clear(); } catch(Exception e) {
            System.out.println("clear(): " + e);
        }
        try { c.add("X"); } catch(Exception e) {
            System.out.println("add(): " + e);
        }
        try { c.addAll(c2); } catch(Exception e) {
            System.out.println("addAll(): " + e);
        }
        try { c.remove("C"); } catch(Exception e) {
            System.out.println("remove(): " + e);
        }
        // The List.set() method modifies the value but
        // doesn't change the size of the data structure:
        try {
            list.set(0, "X");
        } catch(Exception e) {
            System.out.println("List.set(): " + e);
        }
    }
    public static void main(String[] args) {
        List<String> list =
                Arrays.asList("A B C D E F G H I J K L".split(" "));
        test("Modifiable Copy", new ArrayList<String>(list));
        test("Arrays.asList()", list);
        test("unmodifiableList()",
                Collections.unmodifiableList(
                        new ArrayList<String>(list)));
    }
} /* Output:
--- Modifiable Copy ---
--- Arrays.asList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- unmodifiableList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
List.set(): java.lang.UnsupportedOperationException
*///:~

在主函数内先用Arrays这个工具类的asList方法生成了一个List。在调用静态函数test方法时,先后测试了3种List:

  1. ArrayList的构造器,构造器签名是:public ArrayList(Collection<? extends E> c)
  2. Arrays工具类的静态方法asList方法,其方法签名是public static <T> List<T> asList(T... a)
  3. Collections工具类的静态方法unmodifiableList方法,其方法签名是public static <T> List<T> unmodifiableList(List<? extends T> list)

这3种List的定义分别在:

  1. ArrayList
  2. Arrays$ArrayList(注意是Arrays的静态内部类)
  3. Collections$UnmodifiableRandomAccessList(注意是Collections的静态内部类)

在静态方法test中,有一句Collection<String> subList = list.subList(1,8),这个list是传入的形参,其类型是List<String>。而List<E>接口中的subList方法是没有默认实现的,这意味着上面测试的3种List都是把subList方法进行了各自的实现了的。实现的位置分别在:

  1. ArrayList里。
  2. 本应该在Arrays$ArrayList里,但由于没有override,所以实现在Arrays$ArrayList的父类AbstractList里。
  3. Collections$UnmodifiableList里或者Collections$UnmodifiableRandomAccessList里(都是Collections的静态内部类),由于主函数传参是new ArrayList<String>(list))(ArrayList继承了标记接口RandomAccess),所以静态方法unmodifiableList方法返回的是Collections$UnmodifiableRandomAccessList的实例。

关于subList的各自实现本文不会深究,读者只需要知道List<E>接口中的subList方法签名是:List<E> subList(int fromIndex, int toIndex)即可。

静态方法test里,就是去测试类型为List<String>的传入形参list,看它是否对于List<E>接口里的各个可选操作是否都进行了实现。当List<E>的实现类不支持这些可选操作时,其实现会是直接抛出UnsupportedOperationException异常。

从打印结果来看:

  1. ArrayList支持所有的可选操作(因为一个UnsupportedOperationException异常都没有捕获到),所以要想拥有完备的功能,还是得使用ArrayList啊。
  2. Arrays$ArrayList除了set()操作外,都不支持。
  3. Collections$UnmodifiableRandomAccessList都不支持,名副其实的Unmodifiable不可修改。

Arrays.asList()源码

public class Arrays {
    /*省略*/
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);//调用静态内部类的构造器
    }

    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable//看起来和真正的ArrayList好像差不多
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;//内部实现只是个数组

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

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        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;
        }

        @Override
        public E get(int index) {
            return a[index];//依靠数组的取下标操作
        }

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

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            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;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i++) {
                a[i] = operator.apply(a[i]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
    }
    /*省略*/
}

Arrays.asList返回了一个fixed-size固定大小的List,它继承AbstractList时除了实现了必要的get() & size()外,还实现了set()方法,毕竟set()方法不会违背fixed-size这个概念。

但是这个静态内部类没有去overrideadd() & remove(),这意味着它将使用继承于AbstractList的实现——直接抛出UnsupportedOperationException异常。所以在使用Arrays.asList返回的List时一定要注意到,add() & remove()方法我们是不能使用的。

在toArray方法里面,可能会调用到Arrays.copyOf()方法。

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

三目表达式里,先判断(Object)newType == (Object)Object[].class)的返回值,这里是想判断一下传入的Class对象的类型标签是否为Object[],如果是那就好办了,直接创建Object的数组——new Object[newLength]。但其类型标签如果不是Object[],又想创建新数组,那就必须用到Array.newInstance了(注意是反射包里的java.lang.reflect.Array)。

之所以一定要用三目表达式而不直接使用Array.newInstance,我个人猜想是:使用反射来创建数组必定有着效率的问题,而如果类型标签就是Object[],那么就没有必要使用反射了,直接创建Object数组就可以更快执行。

注意在给Arrays.asList传参时,不能将基本类型数组作为参数。

import java.util.Arrays;
import java.util.List;

public class test1 {
    public static void main(String[] args) {
        int[] myArray = { 1, 2, 3 };
        List myList = Arrays.asList(myArray);
        System.out.println(myList.size());
    }
}

在这里插入图片描述
代码打印结果居然是1,而debug也看出来,myList的元素类型居然是int[3]。之所以打印结果不是3、且myList的元素类型不是int,是因为asList 方法签名:

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

可见其形参类型是T的可变参数,虽然java的自动拆装箱配合泛型方法的类型推断,能在单个基本类型时起到作用,但是遇到了基本类型的数组时,自动拆装箱就会失效了,所以List myList = Arrays.asList(myArray)这里,程序认为你只传入了一个参数,且这个参数的类型是int[]

相对的,在单个基本类型时,java的自动拆装箱就可以配合泛型方法的类型推断一起搞事情了,比如List myList = Arrays.asList(4)这句,debug效果如下:
在这里插入图片描述

Collections.unmodifiableList()源码

    public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
    }

unmodifiableList方法先判断形参list是否属于RandomAccess的,RandomAccess是一个标记接口,代表可以随机存取,比如一般List就可以随机访问(其实就是其内部实现是数组,数组取下标操作当然就是随机访问了),而Set就不可以随机访问。根据是否属于RandomAccess,返回相应的实例。

    static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                                  implements List<E> {
        private static final long serialVersionUID = -283967356065247728L;

        final List<? extends E> list;

        UnmodifiableList(List<? extends E> list) {
            super(list);
            this.list = list;
        }

        public boolean equals(Object o) {return o == this || list.equals(o);}
        public int hashCode()           {return list.hashCode();}

        public E get(int index) {return list.get(index);}//非可选操作里,就实现了get
        public E set(int index, E element) {//其他的非可选操作,都是抛异常
            throw new UnsupportedOperationException();
        }
        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public E remove(int index) {
            throw new UnsupportedOperationException();
        }
        public int indexOf(Object o)            {return list.indexOf(o);}
        public int lastIndexOf(Object o)        {return list.lastIndexOf(o);}
        public boolean addAll(int index, Collection<? extends E> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            throw new UnsupportedOperationException();
        }
        @Override
        public void sort(Comparator<? super E> c) {
            throw new UnsupportedOperationException();
        }

        public ListIterator<E> listIterator()   {return listIterator(0);}

        public ListIterator<E> listIterator(final int index) {
            return new ListIterator<E>() {
                private final ListIterator<? extends E> i
                    = list.listIterator(index);

                public boolean hasNext()     {return i.hasNext();}
                public E next()              {return i.next();}
                public boolean hasPrevious() {return i.hasPrevious();}
                public E previous()          {return i.previous();}
                public int nextIndex()       {return i.nextIndex();}
                public int previousIndex()   {return i.previousIndex();}

                public void remove() {//当然,ListIterator也直接抛异常,如果改成调用i成员的remove,可能不会抛异常的
                    throw new UnsupportedOperationException();
                }
                public void set(E e) {
                    throw new UnsupportedOperationException();
                }
                public void add(E e) {
                    throw new UnsupportedOperationException();
                }

                @Override
                public void forEachRemaining(Consumer<? super E> action) {
                    i.forEachRemaining(action);
                }
            };
        }

        public List<E> subList(int fromIndex, int toIndex) {
            return new UnmodifiableList<>(list.subList(fromIndex, toIndex));
        }

        /**
         * UnmodifiableRandomAccessList instances are serialized as
         * UnmodifiableList instances to allow them to be deserialized
         * in pre-1.4 JREs (which do not have UnmodifiableRandomAccessList).
         * This method inverts the transformation.  As a beneficial
         * side-effect, it also grafts the RandomAccess marker onto
         * UnmodifiableList instances that were serialized in pre-1.4 JREs.
         *
         * Note: Unfortunately, UnmodifiableRandomAccessList instances
         * serialized in 1.4.1 and deserialized in 1.4 will become
         * UnmodifiableList instances, as this method was missing in 1.4.
         */
        private Object readResolve() {
            return (list instanceof RandomAccess
                    ? new UnmodifiableRandomAccessList<>(list)
                    : this);
        }
    }

    /**
     * @serial include
     */
    static class UnmodifiableRandomAccessList<E> extends UnmodifiableList<E>
                                              implements RandomAccess
    {
        UnmodifiableRandomAccessList(List<? extends E> list) {
            super(list);
        }

        public List<E> subList(int fromIndex, int toIndex) {
            return new UnmodifiableRandomAccessList<>(
                list.subList(fromIndex, toIndex));
        }

        private static final long serialVersionUID = -2542308836966382001L;

        /**
         * Allows instances to be deserialized in pre-1.4 JREs (which do
         * not have UnmodifiableRandomAccessList).  UnmodifiableList has
         * a readResolve method that inverts this transformation upon
         * deserialization.
         */
        private Object writeReplace() {
            return new UnmodifiableList<>(list);
        }
    }

UnmodifiableList的实现其实就是套一个壳子而已,所以操作都依赖于构造器里传入的那个List。

UnmodifiableList是名副其实的不可修改,那些常用操作里面,它就只实现了get()方法,连set()都不让用。注意Collections$unmodifiableList还继承了Collections$UnmodifiableCollection,后者已经帮忙实现了size() isEmpty() contains() iterator()等方法,当然它的实现风格也是让很多方法抛UnsupportedOperationException异常。

UnmodifiableRandomAccessList的实现就简单,直接继承UnmodifiableList就好了。注意这二者的区别就是:UnmodifiableRandomAccessList代表了可随机访问,UnmodifiableList代表了不可以随机访问。

注意到,UnmodifiableRandomAccessList有一个writeReplace方法必然返回一个非随机访问的不可修改列表;而UnmodifiableList有一个readResolve方法可能返回一个随机访问的不可修改列表。从这两个方法的逻辑可以理解出:随机访问的列表可以直接转换为非随机访问的列表(writeReplace方法),但非随机访问的列表要想转换为随机访问的列表的前提是,此列表原本就是可随机访问的(readResolve方法)。这种关系就类似于父类对象和子类对象之间的互相转换。

posted @ 2019-11-24 16:42  allMayMight  阅读(717)  评论(0编辑  收藏  举报