【java集合框架源碼剖析系列】java源碼剖析之ArrayList

注:博主java集合框架源碼剖析系列的源碼所有基於JDK1.8.0版本。java

本博客將從源碼角度帶領你們學習關於ArrayList的知識。api

一ArrayList類的定義:數組

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
從上述代碼能夠看到:ArrayList繼承自AbstractList同時實現了List,RandomAccess,Cloneable與Serializable接口

二ArrayList類一些重要屬性:數據結構

 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;
從這裏能夠看到ArrayList所有操做是基於Object[]數據結構的,即ArrayList底層是基於數組實現的,所以具有較好的隨機訪問的能力

三ArrayList內部實現原理:咱們先來看一下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的時候,其內部處理過程與LinkedList很是相似,即先將集合轉化爲數組,而後將元素複製到本身內部定義的數組elementData數組中。返回的若不是Object[]將調用Arrays.copyOf方法將其轉爲Object[]。Arrays.copyOf代碼以下:

 public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

四ArrayList一些重要的函數:

1add(E e)與add(int index,E element)框架

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

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

其中add(E e)表示在ArrayList的末尾添加一個元素,而add(int index,E element)表示在index處添加一個元素,其中爲了防止當數組中的容量不夠的狀況,會調用ensureCapacityInternal(size + 1); 函數用來確保向數組中添加元素的時候數組大小始終足夠。代碼以下;

 private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
而在該函數中調用了ensureExplicitCapacity(int minCapacity)函數,咱們來看一下其源碼:

 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)// 超出了數組可容納的長度,則進行動態擴展,即調用grow函數
            grow(minCapacity);
    }
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//將oldCapacity除以2在加上本身,即新的容量爲原來的1.5倍
        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);
    }
綜上,當調用add()添加元素時,add方法先調用ensureCapacityInternal方法增長自身容量(注意此時僅僅是minCapicity的值加1,而數組內存還未擴容),本質上是經過grow()函數來完成的,在調用grow()函數時,先求出原數組的長度記爲oldCapacity,而後將該值更新爲oldCapacity+oldCapacity>>1即新的容量爲原來容量的1.5倍.,將其記爲newCapacity,若是擴容後的容量超出了數組可容納的最大長度MAX_ARRAY_SIZE,則調用hugeCapacity()將其返回值 Integer.MAX_VALUE做爲新的容量,而後調用系統的arraycopy函數將原數組的內容複製到新數組中返回。

即add()方法中存在擴容機制,當經過add()方法添加一個元素時,會先調用ensureCapacityInternal方法來確保數組內存始終足夠,本質上是經過grow函數來完成的,在grow()函數中會將原數組的長度更改成其原來的1.5倍,若是更改後的數組容量比傳入的minCapacity小,則將更改後的容量更新爲minCapacity(該值是指望的數組的最小容量,如當已存在1個元素而後添加一個元素則minCapacity的值爲2即能容納添加進去的元素的最小容量,擴容後的值newCapacity在絕大多數狀況下比它大,但可能比它小,由於上述擴容機制採用的是擴大爲1.5倍,而採用的是整除,如1+1/2=1,而minCapacity=size+1=2,此時newCapacity<minCapacity)dom

2addAll(Collection<? extends E> c) 與addAll(int index, Collection<? extends E> c) 函數

 public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

 public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
能夠看到,addAll()與add()方法幾乎徹底相同,只不過存在一個將集合轉化爲數組的過程,這一點與LinkedList很是相似,而後接下來的操做與add操做徹底相同,即

調用ensureCapacityInternal擴容,若是插入位置不是Arraylist的末尾,則調用系統的arraycopy函數將索引處及其後的元素後移一位在index處插入該元素。處理完畢後將臨時數組a中的元素arraycopy到elementData中。學習

3Object[] toArray() this

 public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

 public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
該函數的做用就是返回一個包含list中全部元素的數組,具體實現過程以下:

若是傳入數組的長度length小於size,返回一個新的數組,大小爲size,類型與傳入數組相同;
不然將elementData的元素所有複製到傳入的數組a中,並返回a;
若length大於size,除了複製elementData外,還將把返回數組的第size個元素置爲空。
4void sort(Comparator<? super E> c)spa

public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

能夠看到該sort函數是基於Comparator<? super E> c接口的,即待排序的元素必須實現Comparator接口,其實不單單是ArrayList,java集合框架中的所有集合的排序都是基於

Comparator接口的。

五總結:

1ArrayList的內部是基於Object[]類型的數組實現的,支持下標訪問,所以具有較高的隨機訪問速度。

2當用一個集合做爲參數來構造一個ArrayList時,其內部是是先將該集合轉化爲數組,這一點與LinkedList很是相似,與HashMap同樣,ArrayList也存在擴容機制,即調用ensureCapacityInternal擴容

3能夠看到ArrayList中的方法都未使用synchronized關鍵字修飾,即ArrayList是非同步的。

4ArrayList容許重複元素存在,由於在add元素的過程當中不存在HashMap中put時判重替換的過程,只是進行簡單的插入操做。
相關文章
相關標籤/搜索