asList
asList是Arrays的一个可以将数组转换为List的方法,具体用法如下:
String[] arr = new String[]{"a","b","c"};
List<String> list = Arrays.asList(arr);
先说结论:
- 原数组和asList得到的list使用的是同一份元素数组的内存空间,因此,无论是在原数组和list修改某元素的值,都会互相影响。
- 得到的ArrayList是Arrays里的一个内部类,调用结构性修改的方法(add,remove)会抛出异常。
Arrays.asList返回的是一个“ArrayList”,但并非我们熟悉的ArrayList,该ArrayList是Arrays里的一个内部类。
该ArrayList同样继承了AbstractList,但只重写(实现)了部分方法,如size,get,set等方法,对于对结构修改的方法如add,remove并没有重写,而是直接继承了原抽象类AbstractList的方法。
因此如果通过Arrays.asList拿到的list直接调用这些方法,会直接抛出UnsupportedOperationException异常。
另外还有一个值得注意的点是,原数组和新得到的list使用的是同一份元素数组的内存空间,因此,无论是在原数组和list修改某元素的值,都会互相影响。
subList
subList() 方法用于截取并返回动态数组中的一部分,类似substring方法。用法如下:
// 创建一个动态数组
ArrayList<String> list= new ArrayList<>();
sites.add("a");
sites.add("b");
sites.add("c");
List<String> subList = list.subList(0, 2);//["a","b"]
先说结论:
- subList是list的一个视图,两个list同一份元素数组,两个list各自调用非结构性修改如set方法均会影响对方。
- subList的结构性修改如add,remove会直接修改双方的结构,但不会有任何问题。
- list的结构性修改如add,remove会造成subList的不可用:get,add,size()等方法调用后均会报错。
subList方法,返回的并不是真正意义上返回和原来list同数据结构的类,ArrayList.subList返回的是一个名为SubList的ArrayList的一个内部类,LinkedList.subList返回的是AbstractList文件里的名为SubList的类,下面为ArrayList里的SubList
下面来分析下第3点的报错:
这个SubList类有个很重要的特点是,在调用几乎所以方法前都会去调用一个校验方法:
就是checkForComodification这个方法,这个方法很简单,就是检查一下外部的ArrayList类的modCount 和 自己的modCount是否一致,不一致直接抛出ConcurrentModificationException异常。
modCount 是什么?它是一个记录着ArrayList结构变化次数的字段,当ArrayList结构修改时会自增。
调用subList方法返回的SubList类会复制外部的ArrayList类的modCount,因此ArrayList进行add,remove结构性变化时就会导致modCount 变化,从而和subList最初记录的modCount 不一致而抛出异常。
而subList自己的结构性修改则是会重置modCount和ArrayList保持一致,所以它的修改不会造成任何异常发生。
父集合的modCount实际上可以理解为记录了自己结构的版本号,子集合任何的操作都要保证自己最初拿到的那份父集合的结构和当前的是一致的,防止结构混乱。
这和在迭代遍历中执行修改结构的操作异常发生类似,迭代器生成的时候也会copy一份modCount命名为expectedModCount,进行迭代操作时会校验当前的List的modCount和expectedModCount是否一致,否则抛出ConcurrentModificationException异常。