平时经常用到ArrayList.subList进行list的截取,发现里面有一些注意点,记录一下
List<Integer> oldList = new ArrayList<Integer>(){{add(1);add(2);add(3);}};
List<Integer> newList = oldList.subList(1, 2);
-
SubList返回的是原list的视图,并不是创建了一个新的集合
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
其中 newSubList是ArrayList里的一个内部类
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;
}
当访问例子中的newList时,是通过调用subList方法的入参作为偏移量来访问以及操作的,比如说get方法:
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
-
修改原集合的值,会影响子集合,因为返回的是原集合的视图
-
修改原集合的结构(add/remove等),遍历新集合时,会抛出ConcurrentModificationException异常
ArrayList中的modCount记录了该list结构性的修改次数,在内部类SubList进行任何操作时,都会校验SubList中的modCount与原集合的modCount是否一致,不一致的话就会抛出上面的
private void checkForComodification() {
if (this.modCount != l.modCount)
throw new ConcurrentModificationException();
}
-
修改子集合的值,会影响原集合,因为本质上修改的仍然是原来的集合
-
修改子集合的结构,会影响原集合。
对子集合进行add操作时,会调用ArrayList的add方法,会将ArrayList中的modCount自增,同时同步到SubList中。remove类似。