Arrays.toList(T[] t)返回的List不可执行add方法
2017-02-20 18:21
387 查看
前言
首先让我们来看一段代码:String[] strings = new String[]{"hello","world"}; List<String> stringList = Arrays.asList(strings); stringList.add("java");
咋眼一看这段代码没什么问题,然而这段带却抛出了一个名为:UnsupportedOperationException的异常。看到异常时有些懵逼,查了资料说只是因为Arrays自身实现的问题。于是乎,我查看了源码。
ArrayList之李逵与李鬼
Arrays.toList(T… t) 方法返回的是Arrays的一个内部类ArrayList,大家可不要被这个名字骗了,此ArrayList非彼ArrayList啊,这完全就是李鬼啊。声明方式
先看一下这个ArrayList的声明方式吧:private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable
我们经常使用的ArrayList的声明方式为:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
再看看AbstractList的声明方式为:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
现在可以看出我们经常使用的ArrayList相比Arrays内部的ArrayList来说多实现了一个Cloneable接口,至于为什么不给Arrays内部的ArrayList实现Cloneable接口,这与当初设计这个内部类的目的是有关系的,后面将会说到这个内部类的作用。
元素存储区
先看看Arrays.ArrayList的元素存储区的声明:private final E[] a;
而java.util.ArrayList的存储区是这样声明的:
transient Object[] elementData;
这么一对比,李鬼假的简直太粗暴了,连一个优雅的名字都不舍得想。从声明上可以看出Arrays.ArrayList中的元素存储数组是一个不可变的数组引用,由于数组的长度本身是不可变的,所以Arrays.ArrayList从元素存储上就不支持长度变化,那么肯定是不允许add和remove操作了,所以在该类中就未对add和remove进行实现,直接调用了父类AbstractList中声明的方法:
public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } public E set(int index, E element) { throw new UnsupportedOperationException(); }
但凡是需要使用到这三个方法的操作均会直接抛出UnsupportedOperationException异常。
java.util.ArrayList之所以可以变长,是因为使用到了Arrays.copyOf(T[] original, int newLength)方法,具体实现:
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
而且此类内部是实现了add、remove和set三个方法:
/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; } /** * Removes the first occurrence of the specified element from this list, * if it is present. If the list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index * <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> * (if such an element exists). Returns <tt>true</tt> if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). * * @param o element to be removed from this list, if present * @return <tt>true</tt> if this list contained the specified element */ public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
在查看源码的过程中也许有同学已经注意到了java.util.ArrayList中使用了Arrays.copy和System.arraycopy,这两者之间的区别以后有机会再深入探讨。
到此我们也应该知道为什么Arrays.toList返回的List不支持add方法了,其根本原因是因为存储元素的数组未不可变数组。
是谁造就了李鬼?
纵观jdk中所有开放的List实现类中没有一个提供以数组为参数的构造方法。从数据结构来看,数组是一种特殊的链表(不可变长),正是因为这个特殊性,造成了在java中的数组无法使用链表(List)的一些方法,使用上与聊表对象的使用方式不同,仅支持设值、遍历与排序。再多的功能,如元素的新增、删除以及子连的获取功能均无法直接调用系统接口,只能自行编写代码。本来都是链表,却因为其中的一些特殊性,变成了两种截然不同的对象,为了打通数组与List之间的堡垒,Arrays.ArrayList诞生了,让我们可以使用更短的代码实现数组与List之间的转换。String[] strings = new String[]{"hello","world"}; List<String> stringList = new ArrayList(Arrays.asList(strings)); stringList.add("java");
Arrays.ArrayList**仅可用于作为构造List的参数**,其他时候均可认为其就是一个数组。
那么我们上面看到了Arrays.ArrayList未实现Cloneable接口,是因为Arrays.ArrayLists的状态本身就不可变,当然其中元素可变,实现之后并无任何意义。
总结
这一个异常的抛出,让我深入jdk源码内部了解了问题的根源,更让我接触到了大牛们写代码的思想:明确类的设计目的,从而明确类的功能边界,在编码的过程中绝不越界完成,也不重复完成功能。相关文章推荐
- Arrays.asList方法返回的List不能执行add、remove操作
- Arrays.asList() 返回的list不能add,remove以及慎用java.util.Collections.copy()方法
- Arrays.asList返回结果执行clear方法抛java.lang.UnsupportedOperationException
- SqlQueryToList 执行SQL语句并把结果返回成一个List<dynamic>
- list执行remove和add方法时,报java.util.ConcurrentModificationException的原因
- Arrays中的方法asList()返回来的list与new ArrayList()区别
- Mac中第一次使用Android Studio出现unable to access android sdk add-on list的解决方法
- 为什么Java里的Arrays.asList不能用add和remove方法?
- 安装android studio时候弹出unable to access android sdk add-on list解决方法
- 第一次安装android studio时候弹出unable to access android sdk add-on list解决方法
- 安装android studio时候弹出unable to access android sdk add-on list解决方法
- 关于 ArrayList.toArray() 和 Arrays.asList().toArray()方法
- Arrays.asList()返回的List不能add和remove
- 第一次安装android studio时候弹出unable to access android sdk add-on list解决方法
- 将数据库返回的ResultSett转换成List装Map形式的方法(ResultSetToList)
- Arrays.asList() 返回的list不能add,remove
- 将数组转为List的方法,转换后不可执行List的哪些操作?
- Arrays.asList(param)方法中param的不同,方法的返回值不同的实例分析
- 第一次安装android studio时候弹出unable to access android sdk add-on list解决方法
- 第一次安装Android studio时候弹出unable to access android sdk add-on list解决方法