在项目中,遇到了这个方法,之前没有遇见过,现在记录一下。
源码:
/**
* Returns a view of the portion of this list between the specified
* fromIndex, inclusive, and toIndex, exclusive. (If
* fromIndex and toIndex are equal, the returned list is
* empty.) The returned list is backed by this list, so non-structural
* changes in the returned list are reflected in this list, and vice-versa.
* The returned list supports all of the optional list operations supported
* by this list.
*
返回此list中指定的 fromIndex(包括)和 toIndex (不包括)之间的部分视图。 (如果 fromIndex 和 toIndex 相等,则返回的list为空。)返回list中的非结构更改将反映在这个list,反之亦然。返回的list支持此lsit支持的所有可选操作
* This method eliminates the need for explicit range operations (of
* the sort that commonly exist for arrays). Any operation that expects
* a list can be used as a range operation by passing a subList view
* instead of a whole list. For example, the following idiom
* removes a range of elements from a list:
* {@code
* list.subList(from, to).clear();
* }
* Similar idioms may be constructed for indexOf and
* lastIndexOf, and all of the algorithms in the
* Collections class can be applied to a subList.
*
对subList后得到的list的操作相当于对原list进行操作,例如想删除list中下标10到20的数据,可以采用list.subList(10, 21).clear();
方法。
* The semantics of the list returned by this method become undefined if
* the backing list (i.e., this list) is structurally modified in
* any way other than via the returned list. (Structural modifications are
* those that change the size of this list, or otherwise perturb it in such
* a fashion that iterations in progress may yield incorrect results.)
*
如果原有list发生了结构变化,不然可能会出现错误的结果
* @param fromIndex low endpoint (inclusive) of the subList
* @param toIndex high endpoint (exclusive) of the subList
* @return a view of the specified range within this list
* @throws IndexOutOfBoundsException for an illegal endpoint index value
* (fromIndex < 0 || toIndex > size ||
* fromIndex > toIndex)
*/
List subList(int fromIndex, int toIndex);
下面看看ArrayList
中的实现方法:
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
// 内部类
private class SubList extends AbstractList<E> implements RandomAccess
// 只列出构造方法
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;
}
// 随便看一个方法,例如add
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
可以看出来,返回的list,执行add(int index, E e)
方法,实际上是对parent
,也就是对原list进行了操作。
其中有个特别的地方checkForComodification();
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
解释下ArrayList.this
代表什么:
有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名。
所以ArrayList.this
指的就是原有list,那么对原有list进行操作,modCount
会怎么变化呢?看原有list的add(E e)
方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
由于内部类也继承了AbstractList
,所以本身也有一个modCount
,原有list操作,只是修改了原list的modCount
,不会影响内部类的modCount
属性,从而导致了ConcurrentModificationException
。
看例子:
public static void main(String[] args) {
int i = 0;
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
List<Integer> subList = list.subList(1, 3);
list.add(6);
System.out.println(subList.size());
}
运行结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
at java.util.ArrayList$SubList.get(ArrayList.java:1043)
at cn.ybt.util.ListUtils.main(ListUtils.java:78)
最后看一下注释中的list.subList(from, to).clear();
方法:
并没有clear()
方法, 查看父类AbstractList
中的clear
方法:
public void clear() {
removeRange(0, size());
}
调用的是SubList
类中的removeRange(int fromIndex, int toIndex)
方法:
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
可以看到,最终调用的是原有list的removeRange(int fromIndex, int toIndex)
方法。
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
由于方法是protected
修饰的,所以原本的list不能调用这个方法,只能采用clear()
方法进行调用。
所以list.subList(from, to).clear();
方法带来了很大的便利。
ok,收工下班~