ArrayList的subList会出现的异常
【阿里巴巴手册规定】
在 subList 场景中, 高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生
ConcurrentModificationException 异常。
1、subList方法
理解:ArrayList 的方法 subList() 就是分割集合的一段出来使用
示例:
package com.song.list;
import java.util.ArrayList;
import java.util.List;
public class Sublist {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
//注意:(0,2) 是 左闭又开 取值 ,取的是索引对应的 0,1 两个元素
List<String> subList = list.subList(0, 2);
//subList 即包含了元素 "aa"和"bb"
for (String s : subList) {
System.out.println(s);
}
}
}
错误示例:
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(6);
List<Integer> list = arrayList.subList(1, 3);
for (Integer integer : list) {
System.out.println(" "+integer);
}
System.out.println("wo modify arrayList");
arrayList.remove(0);
for (Integer integer : list) {
System.out.println(" "+integer);
}
}
日志输出如下:
为什么会这样呢?
AbstractList
// 定义了一个modCount属性。
protected transient int modCount = 0;
因为ArrayList的父类AbstractList中有个protected transient int modCount = 0;
modCount 的作用是用来记录当前ArrayList被修改的次数。
比如add(),remove()等都会导致modeCount增加
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;
}
(1) SubList 继承 AbstractList ,所以具有List接口的所有方法。
(2) SubList 是ArrayList 的一个内部类。SubList并没有重新创建一个List,而是直接引用原有的List,只不过对原来List做截取而已。
(3) ArrayList 也是继承AbstractList,但是 SubList 和 ArrayList 没有继承关系,所以不能强制转换。
ArrayList.subList()会生成一个SubList的对象,SubList中有个对应modCount同步ArrayList中的modeCount:
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
protected transient int modCount = 0;
...
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
SubList对象每次再遍历时,会将自己的modeCount与ArrayList的modeCount进行对比,如果两个值不一样就会报异常:ConcurrentModificationException
public ListIterator<E> listIterator(final int index) {
checkForComodification();
...
}
...
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
总结:
1、subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。
2、在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException 异常。