1. 程式人生 > >ArrayList原始碼分析(JDK1.8)

ArrayList原始碼分析(JDK1.8)

不積跬步,無以至千里;不積小流,無以成江海。從基礎做起,一點點積累,加油!

ArrayList的定義

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • 從ArrayList<\E>可以看出它是支援泛型的,它繼承自AbstractList,實現了List、RandomAccess、Cloneable、java.io.Serializable介面。

Collection介面的方法:

List
定義

public interface List<E> extends Collection<E>

List介面方法:

AbstractList提供了List介面的預設實現(個別方法為抽象方法(get方法))。

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
//ArrayList中使用的modCount就是AbstractList中宣告的
protected transient
int modCount = 0;

AbstractList中對於List集合的判斷equals()方法

public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while
(e1.hasNext() && e2.hasNext()) { E o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); }

這個方法我覺得寫得特別好,放在這裡留著。。。

  • AbstractList繼承了AbstractCollection類,實現了List集合的除了get方法之外的所有方法:
  • RandomAccess是一個標記介面,介面內沒有定義任何內容。
  • 實現了Cloneable介面的類,可以呼叫Object.clone方法返回該物件的淺拷貝。
  • 通過實現 java.io.Serializable 介面以啟用其序列化功能。未實現此介面的類將無法使其任何狀態序列化或反序列化。序列化介面沒有方法或欄位,僅用於標識可序列化的語義。

ArrayList的資料域

    transient Object[] elementData; // non-private to simplify nested class access
    /**
     * The size of the ArrayList (the number of elements it contains).
     */
    private int size;

transient 關鍵字

  • Java的Serializable提供了一種序列化物件例項的機制。當序列化物件時,可能有一個特殊的物件資料成員,我們不想用Serializable機制來儲存它。為了在一個特定物件的一個域上關閉Serializable,可以在這個域前加上關鍵字transient。
  • transient是Java語言的關鍵字,用來表示一個域不是該物件序列化的一部分。當一個物件被序列化的時候,transient型變數的值不包括在序列化的表示中,然而非transient型的變數是被包括進去的。

看個列子:

public class Person implements Serializable{

    private static final long serialVersionUID = -3319659493471806232L;
    private String name;
    private transient String pass;

    public Person(String name, String pass) {
        this.name = name;
        this.pass = pass;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", pass=" + pass + "]";
    }
}

public class TestTransient {
    public static void main(String[] args) {
        Person person = new Person("Tome", "1234");
        System.out.println(person);
        try {
            // 序列化,被設定為transient的屬性沒有被序列化
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
                    "person.out"));
            o.writeObject(person);
            o.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 重新讀取內容
            ObjectInputStream in = new ObjectInputStream(new FileInputStream(
                    "person.out"));
            Person readPerson = (Person) in.readObject();
            // 讀取後psw的內容為null
            System.out.println(readPerson.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**執行結果
*Person [name=Tome, pass=1234]
*Person [name=Tome, pass=null]
*/

被標記為transient的屬性在物件被序列化的時候不會被儲存。

構造方法分析

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final Object[] EMPTY_ELEMENTDATA = {};

public MyArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public MyArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "
                + initialCapacity);
    }
}

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

三個構造方法:

  • 無參構造方法
    • 使用無參構造方法建立的是一個length為0的內部陣列,當第一次插入資料時,會對其擴容,擴容為預設的大小10
  • 帶有初始容量的構造方法
    • 建立一個預設容量的陣列
  • 帶有初始集合的構造方法

add(E e)方法

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 檢查是否需要擴容
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    //如果建立ArrayList時用的是無參構造器,則第一次插入時會進行一次擴容並且擴到預設陣列大小10
    //然而如果是使用帶有容量引數的構造器且initialCapacity=0時,則不會進行這一步,直接擴充套件陣列容量為minCapacity
    //這就導致一個結果,擴容之後帶引數的構造器的陣列length為1,而預設的為10
    //所以,帶容量引數的初始化時注意自己需要的大小
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

容量的拓展將導致陣列元素的複製,多次拓展容量將執行多次整個陣列內容的複製。若提前能大致判斷list的長度,呼叫ensureCapacity調整容量,將有效的提高執行速度。

可以理解提前分配好空間可以提高執行速度,但是測試發現提高的並不是很大,而且若list原本資料量就不會很大效果將更不明顯。

add(int index, E element)

    public void add(int index, E element) {
        rangeCheckForAdd(index);//檢查index是否越界

        ensureCapacityInternal(size + 1);  // 確保容量
        System.arraycopy(elementData, index, elementData, index + 1,//將index以後的元素後移一個
                         size - index);
        elementData[index] = element;
        size++;
    }
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)//元素可以新增在最後尾端
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //本地的C/C++庫方法,直接操縱記憶體,速度更快
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

indexOf(Object o)

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

remove(int index)

public E remove(int index) {
    rangeCheck(index);//檢查是否越界

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // 將物件的引用置為null,讓垃圾回收器可以回收

    return oldValue;
}
//刪除等其他操作和插入不同,插入可以將元素插在最後面,而刪除等操作是在原有的元素上做修改,所以範圍檢查不一樣
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

clear()

public void clear() {
    modCount++;

    // 清空讓垃圾回收器清理
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

clone()

返回此 ArrayList 例項的淺表副本。(不復制這些元素本身。)

    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

呼叫父類的clone方法返回一個物件的副本,將返回物件的elementData陣列的內容賦值為原物件elementData陣列的內容,將副本的modCount設定為0。

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

執行過程是將elementData從toIndex位置開始的元素向前移動到fromIndex,然後將toIndex位置之後的元素全部置空順便修改size。

        ArrayList<Person> arrayList = new ArrayList<Person>();
        arrayList.add(new Person(2));
        arrayList.add(new Person(3));
        arrayList.add(new Person(4));
        arrayList.add(new Person(5));
        arrayList.add(new Person(6));
        arrayList.subList(2, 4).clear();

上面這段程式碼的執行結果與removeRange(2, 4)一樣,原因是:

arrayList.subList(2, 4)建立一個AbstractList的子類SubList的例項,呼叫clear方法時由於子類SubList沒有重寫,所以直接呼叫AbstractList的clear方法,然而,在AbstractList的clear方法中,呼叫的是removeRange方法,該方法在SubList中重寫了;所以,呼叫的是子類的removeRange,在SubList中有一個指向外部類的指標parent,在removeRange方法中通過parent呼叫了外部類的removeRange方法。所以經過輾轉呼叫,最終呼叫的還是外部類的removeRange方法

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
    //SubList類中沒有重寫AbstractList的clear方法
    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;
        }
        ...
        protected void removeRange(int fromIndex, int toIndex) {
            checkForComodification();
            parent.removeRange(parentOffset + fromIndex,parentOffset + toIndex);
         //呼叫外部類的removeRange(),所以最終修改的就是外部類陣列的元素,即相當於呼叫了外部類的removeRange方法
            this.modCount = parent.modCount;
            this.size -= toIndex - fromIndex;
        }
    }
    //AbstractList類中申明的clear()方法
    public void clear() {
        removeRange(0, size());//由於SubList重寫了removeRange方法,所以呼叫SubList的removeRange方法,
    }

每一次對陣列做新增元素,刪除元素等改變陣列大小或者容量的操作時都會引起對modCount的加1, 究竟這個值有什麼作用呢?

//返回一個遍歷的指標
public Iterator<E> iterator() {
    return new Itr();
}
private class Itr implements Iterator<E> {
    int cursor; // 下一個要返回的元素的下標
    int lastRet = -1; // 最後一個元素的下標
    int expectedModCount = modCount;  //這個的作用就是當用itr來遍歷集合時,不允許往裡面新增或者刪除元素

    public boolean hasNext() {//hasNext方法不會檢查expectedModCount 的值,不會引發ConcurrentModificationException異常
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {//next方法在最開始的時候就會使用checkForComodification進行檢查
    //所以如果在呼叫next之前對集合進行了改變則會丟擲異常
        checkForComodification();//檢查是否改變
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = MyArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
    //remove方法首先會呼叫checkForComodification進行檢查
    //同時,在MyArrayList.this.remove(lastRet)時會改變modCount,expectedModCount = modCount
    //又讓情況變為正常,所以刪除之後這兩個的值又會想等,在之後呼叫next的方法時不會炮竹異常
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            MyArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = MyArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = MyArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

ArrayList的三種遍歷

  • 使用下標(在遍歷的時候可以對集合進行新增刪除)
        MyArrayList<Integer> array = new MyArrayList<Integer>();
        array.add(1);
        array.add(2);
        array.add(1, 3);
        array.display();

        for(int i = 0; i < array.size(); i++){
            System.out.println(array.get(i));
            array.remove(new Integer(3));
            if (i == 1) {
                array.add(1);
            }
        }
  • 使用iterator
        Iterator<Integer> it = array.iterator();
        while(it.hasNext()){
            //array.add(4);  會導致modCount發生變化,從而導致next方法的checkModCount丟擲異常
            int value = it.next();
            //array.remove(new Integer(value));同樣會導致modCount發生變化
            it.remove();//但是可以這樣,因為Iterator的remove方法刪除元素之後對modCount又進行了復原
            System.out.println(value);
        }
  • 使用forEach迴圈(其實本質就是利用的Iterator迴圈)先是it.hasNext(),然後在將value = it.next(),所以執行這段程式碼時,會先列印第一個值,在進行第二輪迴圈的value = it.next()時丟擲異常
        for(Integer value : array){
            //array.add(4);//發生錯誤
            //array.remove(value);
            System.out.println(value);
        }