集合源碼學習之路---ArrayList

ArrayList簡單介紹

ArrayList底層數據結構使用的是數組,也就是線性表的順序存儲結構,是一段連續的存儲單元。具備存取快,增刪慢的特色。ArrayList不是線程安全的java

類定義

從類定義上看,arrayList是支持泛型的,繼承自AbstractList,實現了List接口。同時實現了Serializable接口,由於它支持序列化,支持序列化傳輸。實現了Cloneable接口,能夠被克隆。實現了RandomAccess接口,能夠被快速訪問,實際上就是經過下標進行訪問,RandomAccess只是一個標記,無任何定義。設計模式

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

類屬性

private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;

serialVersionUID:是實現了Serializable可自動生成的。簡單來講,Java的序列化機制是經過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常。數組

EMPTY_ELEMENTDATA & DEFAULTCAPACITY_EMPTY_ELEMENTDATA :其實兩個都是一個空數組,只不過再明確長度爲0時,會用EMPTY_ELEMENTDATA,無參構造調用會用DEFAULTCAPACITY_EMPTY_ELEMENTDATA.安全

DEFAULT_CAPACITY:當沒有指定容量,第一次添加元素,arraylist的初始容量.數據結構

elementData:arraylist的buffer,arrayList的容量就是elementData的容量。當arraylist爲空時,elementData就是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,當添加第一個元素的時候,會自動擴容DEFAULT_CAPACITY(10)長度.注意到,它被transient修飾,也就是不參與序列化,只存在調用者的內存當中。dom

size:arraylist含有元素的個數。函數


構造函數

public ArrayList(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() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
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;
        }
    }

arraylist一共定義了三個構造函數。學習

  1. 構造一個指定大小的elementDatathis

  2. 初始化一個默認數組DEFAULTCAPACITY_EMPTY_ELEMENTDATA線程

  3. 將一個集合Collection初始化爲arrayList


核心方法

/**
trim就是去除兩段空格的意思,這個方法是用來給elementdata瘦身用,將佔據的多餘的空間給釋放掉
*/
public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
/**
確保容器容量,若爲空,minCapacity去當前值和默認容量的最大值,而後判斷是否進行擴容
*/
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
/**
minCapacity大於數組長度,則調用grow函數進行擴容
*/
private void ensureExplicitCapacity(int minCapacity) {
        /**
        modCount:是父類AbstractList的變量。集合中全部用到modCount的都是線程不安全的。在作對線性表結構修改的操做會對modCount進行+1,當咱們調用iterator,會檢測modCount是否是咱們指望的,若是在調用期間modCount又發生了變化,iterator將拋出異常.
        */
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
/**
獲取到數組長度,對其進行擴容,增長將近一半的容量,使用Arrays.copyOF進行擴容
*/
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);
    }
//返回容器元素個數
public int size() {
        return size;
    }
//判斷是否爲空
public boolean isEmpty() {
        return size == 0;
    }
/**
兩個方法都是判斷索引正否越界
*/
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * A version of rangeCheck used by add and addAll.
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
/**
* 此方法用戶往線性表最後一位插入元素,若容器容量足夠,則時間複雜度是O(1),若須要擴容,則時間複雜度是O(n)
*/
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
/**
* 此方法用於向指定位置插入元素,時間複雜度爲O(n)
*    1.判斷角標是否越界  2.保證數組容量足夠,不夠經過新建數組擴容 3.從數組指定索引複製直到最後一位,全*部加一
*/
public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
//刪除制定索引元素,需算出位移量,將index以後的其餘元素提早一位,其複雜度爲o(n)
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; // clear to let GC do its work

        return oldValue;
    }
    //遍佈elementData,若值相同,則調用fastRemove移除
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    //此方法和remove大體相同,只是少了判斷索引越界的問題,由於在被調用前已經經過遍歷方式驗證了
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

迭代器

什麼是迭代器呢?其實就是用來遍歷容器內部分或所有元素的對象,每一個迭代器表明容器中肯定的地址,咱們把具備相似行爲的都叫迭代器。雖然迭代器不經常使用,可是裏面的一些知識點,設計模式咱們仍是要學習如下的。arraylist迭代器分爲Iterator和ListIterator。Iterator接口只定義了遍歷和刪除的職責,ListIterator繼承於Iterator,新增了add和set方法,方便咱們在迭代的時候增長刪除元素。須要注意的是,在使用迭代器期間,若使用非迭代器對容器進行數據結構上的改變,將會經過checkForComodification()報錯。

ListIterator的實現以下:

private class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            super();
            cursor = index;
        }

        public boolean hasPrevious() {
            return cursor != 0;
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor - 1;
        }

        @SuppressWarnings("unchecked")
        public E previous() {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[lastRet = i];
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

總結

arraylist源碼方法實在是過多,沒法一一貫你們解釋,不過相信你們理解上面的,再去研究剩下的內容應該沒有多大問題。經過學習源碼,咱們能夠更清楚的知道arraylist在操做時,底層結構發生了哪些變化,爲何arraylist適合存取,不適合增刪.

相關文章
相關標籤/搜索