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

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

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

一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)

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() 

 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)

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時判重替換的過程,只是進行簡單的插入操作。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章