以前在项目中遇到过这么一个问题,就是用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);