List 接口中的 subList(int fromIndex, int toIndex) 方法

在项目中,遇到了这个方法,之前没有遇见过,现在记录一下。

源码:

/**
     * 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();方法:
List 接口中的 subList(int fromIndex, int toIndex) 方法_第1张图片
并没有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(int fromIndex, int toIndex) 方法_第2张图片
所以list.subList(from, to).clear();方法带来了很大的便利。

ok,收工下班~

你可能感兴趣的:(实际项目中遇到的问题)