1)场景:
Arrays.asList()
方法很方便地为我们快捷创建一个 List 集合,比如:
List<String> list = Arrays.asList("123", "456", "789");
但是,如果我们往生成的 list 添加元素时:
list.add("2233");
会抛出 java.lang.UnsupportedOperationException
异常:
java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
也就是说,使用 Arrays.asList
生成的 List 集合不允许添加元素。
2)原因:
要找出原因,我们需要回到源码当中,进入 atList
源码:
// atList 方法源码
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
// Arrays 的静态内部类 ArrayList
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
......
}
从源码中可以看出,Arrays.asList
返回的是 Arrays 类中的静态内部类 ArrayList 对象,并非是 java.util
包下的 ArrayList 类对象。
虽然这个静态内部类 ArrayList 同样是继承了 AbstractList
类,但它只实现了 get
、set
、contain
、indexOf
等方法,却没有实现 add
、remove
、clear
等方法来支持添加、删除操作。
3)总结:
当我们使用 Arrays.asList
把数组转换成集合时,不能使用其修改集合的相关方法,一般只是适用于遍历访问操作。
Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍然是数组,如果修改了原数组数据,其生产的 List 集合数据也会跟着一起被修改:
// 原数组数据
String[] strs = new String[]{"123", "456", "789"};
// 生成 List 集合
List<String> list = Arrays.asList(strs);
System.out.println(list.toString());
// 修改原数组数据
strs[0] = "666";
System.out.println(list.toString());
>>>>>>
[123, 456, 789]
[666, 456, 789]
下面是一个简单的 subList
示例:
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);
System.out.println(subList);
>>>>
[111, 222]
可以看出,subList
的索引是从 fromIndex
(包含)到 toIndex
(不包含)。
我们进去看一下 ArrayList 的 subList 方法源码:
// subList 方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
// ArrayList 内部类 SubList
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
......
}
可以发现,subList
返回的是 ArrayList
的成员内部类 SubList
,如果强转成 ArrayList
的话会抛出异常:java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
。
1)非结构性修改会相互影响
究其原因,subList 返回的内部类 SubList 是 ArrayList 的一个视图,对 SubList 子列表的所有操作最终都会映射到原列表上(对原列表的操作也会影响子列表),如下示例:
// 创建父子列表
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);
System.out.println(list);
System.out.println(subList);
// 修改原列表
list.set(0, "666");
System.out.println(list);
System.out.println(subList);
// 修改子列表
subList.set(0, "888");
System.out.println(list);
System.out.println(subList);
>>>>>
[111, 222, 333]
[111, 222]
---------------
[666, 222, 333]
[666, 222]
---------------
[888, 222, 333]
[888, 222]
2)对父集合的增加或删除(结构性修改),都会导致子列表的遍历、增加、删除产生 ConcurrentModificationExcetption
异常
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);
// 原列表添加元素
list.add("666");
System.out.println(list); // 遍历父列表
System.out.println(subList); // 遍历子列表
>>>>
[111, 222, 333, 666]
java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
at java.util.AbstractList.listIterator(AbstractList.java:299)
at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
at java.util.AbstractCollection.toString(AbstractCollection.java:454)
从上面可以看出,对原集合进行添加操作(结构性修改),会导致遍历子集合时引起ConcurrentModificationExcetption
异常。
注意:ConcurrentModificationExcetption 异常并不是在添加元素时发生的,而是在添加元素后,遍历子集合时发生的。
3)对子列表结构性修改会影响原列表但不会触发异常
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);
// 子列表添加元素
subList.add("666");
System.out.println(list);
System.out.println(subList);
>>>>>
[111, 222, 666, 333]
[111, 222, 666]
从上面可以看出,对子列表的结构性修改会影响到父列表,但遍历的时候不会引起异常。
4)总结
ArrayList 的 subList 方法,返回的是原集合的一个子集合(视图),非结构性修改任意一个集合的元素的值,都会彼此影响;结构性修改原集合时,会报 ConcurrentModificationException
异常,而结构性修改子集合时,会影响原集合,但不会报异常。