ArrayList的subList返回仅仅只是一个视图:
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
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 E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
……
subListRangeCheck方式是判断fromIndex、toIndex是否合法,如果合法就直接返回一个SubList对象,注意在产生该new该对象的时候传递了一个参数this,它代表着原始list。
该SubLsit是ArrayList的内部类,它与ArrayList一样,都是继承AbstractList和实现RandomAccess接口。同时也提供了get、set、add、remove等list常用的方法。
从它的set方法可以看出,对SubList的修改实质上是修改了原始list。
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
List<String> subList = list.subList(1, 3);
System.out.println(list); // [a, b, c]
System.out.println(subList); // [b, c]
subList.add("d");
System.out.println(list); // [a, b, c, d]
System.out.println(subList); //[b, c, d]
subList.set(2, "e");
System.out.println(list); // [a, b, c, e]
System.out.println(subList); // [b, c, e]
subList生成子列表后,不要试图去操作原列表:
System.out.println(list.size());
System.out.println(subList.size());
list.add("f");
System.out.println(list.size());
System.out.println(subList.size());
在sublist.size()调用时会抛出java.util.ConcurrentModificationException
,这是因为:
public int size() {
checkForComodification();
return this.size;
}
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
这是由于fail-fast机制,对原列表有改动时,原列表的modCount会有变化,但是不会反映给子列表的modCount,而修改子列表时,实际上是修改了原列表,但是原列表的modCount修改后会重新复制给子列表的modCount。因此操作子列表没问题,但是操作原列表后,在使用子列表会抛出该异常。
推荐使用subList处理局部列表:比如有一个列表存在1000条记录,需要删除100-200位置处的数据:
list1.subList(100, 200).clear();