1. 程式人生 > >帶你走進Java集合_ArrayList原始碼深入分析4

帶你走進Java集合_ArrayList原始碼深入分析4

上一篇我們主要講解了ArrayList的迭代器,我們首先歸納一下ArrayList迭代器的主要內容:

1.迭代器主要利用遊標cursor來遍歷集合的,遊標cursor主要指向下一個元素的下標。所以cursor是關鍵。
2.迭代器在迭代的時候可以察覺到fast-fail.
3.Itr的遊標只能向後移動,所以只能向後遍歷,而ListIterator既可以向前移動也可以向後移動

這一篇文章我們從原始碼角度講解ArrayList的最後一個知識點:SubList

從字面上可以知道是ArrayList的子元素,所以SubList也會提供刪、改、查,迭代的功能,我們要牢記一個知識點:subList不是從ArrayList複製一份,而是SubList指向的例項是ArrayList的一段對映,ArrayList的修改和SubList的修改是對同一個底層陣列的修改

。在ArrayList獲取subList的方法如下:

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

上面的方法有兩個引數,fromIndex是對映的開始下標,toIndex是對映的結束下標。其中0<=fromIndex<=toIndex<=size,此方法最後一個元素不包含toIndex,如果fromIndex,toIndex不符合

舉例說明:

public static void main(String[] args) throws Exception {
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        List<Integer> subList = list.subList(1, 4);
        for (Integer sub : subList) {
            System.out.print(sub + " ");
        }
    }

執行結果如下:


從運算結果中我們可以看到,subList方法包含fromIndex,但是不包含toIndex.

如果fromIndex、toIndex不符合0<=fromIndex<=toIndex<=size公式時,運算的結果是怎樣的,例如我們formIndex=1,toIndex=10,運算結果:


所以,呼叫ArrayList的subList(fromIndex,toIndex)方法時,要時刻記住0<=fromIndex<=toIndex<=size這個公式。

我總結了SubList有一下兩個特性:

特性1.SubList是ArrayList集合的一段對映,而不是拷貝。
特性2.要牢記這個公式:0<=fromIndex<=toIndex<=size

現在我們從SubList的原始碼來分析上面的的兩個特性

SubList是ArrayList的一個內部類,

1.SubList的重要屬性如下:

第一個重要的屬性:AbstractList<E> parent:這個就是ArrayList

第二個重要的屬性:int parentOffset:對映到ArrayList的開始下標

第三個重要的屬性:int offset:subList的開始下標,其實在ArrayList中parentOffset=offset.從下面原始碼可以證明:

 public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
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;
        }

引數offset=0,所以parentOffset=offset

SubList提供瞭如下方法,總結一下,呼叫這些方法時,都不能修改ArrayList,否則就會丟擲異常。每一個方法都會首先呼叫checkForComodification()檢查一下:

private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }

第一個方法:E set(int index, E e)

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;
        }

1)rangeCheck(index)就是判斷給出的索引是否有效

2)checkForComodification()就是判斷呼叫set的時候是否修改的ArrayList元素。

3)從第三句可以看出,直接修改ArrayList集合的元素

從這個方法中就可以證明特性1所說的,SubList是ArrayList一段對映,而不是複製。

下面的方法和set類似,只要讀過ArrayList的同學都不難理解,在這裡就不做解釋了。

這篇文章主要介紹了ArrayList中的一個方法subList(fromIndex,toIndex),我們記住SubList的兩個特性後,就能夠駕馭subList方法了

特性1.SubList是ArrayList集合的一段對映,而不是拷貝。
特性2.要牢記這個公式:0<=fromIndex<=toIndex<=size