JDK 之 Arrays.asList - 源码分析
Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List 。
其源代码如下:
@SafeVarargs public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
问题发现
根据上述方法的描述,我们先来编写几个例子:
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类。
其源代码如下:
1 /** 2 * @serial include 3 */ 4 private static class ArrayList<E> extends AbstractList<E> 5 implements RandomAccess, java.io.Serializable 6 { 7 private static final long serialVersionUID = -2764017481108945198L; 8 private final E[] a; 9 10 ArrayList(E[] array) { 11 if (array==null) 12 throw new NullPointerException(); 13 a = array; 14 } 15 16 public int size() { 17 return a.length; 18 } 19 20 public Object[] toArray() { 21 return a.clone(); 22 } 23 24 public <T> T[] toArray(T[] a) { 25 int size = size(); 26 if (a.length < size) 27 return Arrays.copyOf(this.a, size, 28 (Class<? extends T[]>) a.getClass()); 29 System.arraycopy(this.a, 0, a, 0, size); 30 if (a.length > size) 31 a[size] = null; 32 return a; 33 } 34 35 public E get(int index) { 36 return a[index]; 37 } 38 39 public E set(int index, E element) { 40 E oldValue = a[index]; 41 a[index] = element; 42 return oldValue; 43 } 44 45 public int indexOf(Object o) { 46 if (o==null) { 47 for (int i=0; i<a.length; i++) 48 if (a[i]==null) 49 return i; 50 } else { 51 for (int i=0; i<a.length; i++) 52 if (o.equals(a[i])) 53 return i; 54 } 55 return -1; 56 } 57 58 public boolean contains(Object o) { 59 return indexOf(o) != -1; 60 } 61 }
从这个内部类ArrayList的实现可以看出,它继承了抽象类java.util.AbstractList<E>, 但是没有重写add和remove方法,没有给出具体的实现。
但是,默认情况下,java.util.AbstractList类在add、set以及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异常。
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的例子:
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,一个比较简单的方法如下:
重新使用java.util.ArrayList包装一层。
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~~~]