java集合源碼分析之ArrayList

源碼分析以JDK1.8爲例.html

ArrayList就是動態數組,就是Array的複雜版本.它提供了動態的增長和減小元素,實現了Collection和List接口,靈活的設置數組的大小等好處.UML圖以下:java

集合類圖

源碼解讀

公共屬性

//默認容量
    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;

   //最大數組長度
   //一些虛擬機會在數組頭部保存頭信息,佔用更多空間,致使OOM
   private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

無參構造器

//初始化一個空數組
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

int 構造器

指定初始容量,大於0直接初始化;等於0初始化一個空數組,小於0拋出異常。算法

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

集合構造器

集合參數轉化爲對象數組賦值給元素集合,若是有元素判斷集合類型,若是不是Object[] 執行集合複製轉化爲Object[]. 沒有元素直接返回空集合.數組

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 {
            //初始化爲空集合
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

有一個註釋,c.toArray 可能不會返回Object[].讓參考bug編號6260652.到oracle 官網查看bug https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652安全

大意就是Arrays.asList 建立集合,再調用toArray返回的對象不是Object[]類型。測試一下看看:oracle

public void test(){
        List<Integer> obj = new ArrayList<>();
        obj.add(32);
        obj.add(344);
        List<Integer> ls = new ArrayList<>(obj);
        System.out.println(ls.size());
        Object[] c = obj.toArray();
        System.out.println(c.getClass());
        System.out.println(Object[].class);
        //Arrays.asList建立
        List<Integer> d = Arrays.asList(3,2,3);
        System.out.println(d.toArray().getClass());
    }
2
class [Ljava.lang.Object;
class [Ljava.lang.Object;
class [Ljava.lang.Integer;

可見Arrays.asList 返回的是Integer[],並不是Object[]. 查看Arrays相關源碼dom

//初始化ArrayList
    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

此處的ArrayList爲Arrays的內部類.jvm

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        //泛型數組
        private final E[] a;
        
        //初始化
        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }
        
        //調用a的clone方法
        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }
}

官方顯示,JDK9已經解決。 擴展一下,查看數組的clone方法,length屬性的實現.ide

public void test(){
        int [] a = {1,2,3};
        System.out.println(a.length);
    }

    public  static  void test2(){
        Integer[] f = {3,4};
        f.clone();

    }

查看byte code源碼分析

public class com.xwolf.ArrayListTest {
  public com.xwolf.ArrayListTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: iconst_3
       1: newarray       int
       3: dup
       4: iconst_0
       5: iconst_1
       6: iastore
       7: dup
       8: iconst_1
       9: iconst_2
      10: iastore
      11: dup
      12: iconst_2
      13: iconst_3
      14: iastore
      15: astore_1
      16: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      19: aload_1
      20: arraylength
      21: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      24: return

  public static void test2();
    Code:
       0: iconst_2
       1: anewarray     #4                  // class java/lang/Integer
       4: dup
       5: iconst_0
       6: iconst_3
       7: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      10: aastore
      11: dup
      12: iconst_1
      13: iconst_4
      14: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      17: aastore
      18: astore_0
      19: aload_0
      20: invokevirtual #6                  // Method "[Ljava/lang/Integer;".clone:()Ljava/lang/Object;
      23: pop
      24: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #7                  // Method test2:()V
       3: return
}

此處不展開,arraylength、invokevirtual參考官網java虛擬機規範https://docs.oracle.com/javase/specs/jvms/se8/html/index.html.

add方法

//List後追加元素
public boolean add(E e) {
        //擴容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

 private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(ca lculateCapacity(elementData, minCapacity));
    }
//計算容量,元素爲空返回默認容量和容量的最大值
 private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
        } 

//擴容到指定容量,修改次數modCount加1
 private void ensureExplicitCapacity(int minCapacity) {
        //增長修改次數
        modCount++;
        
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

private void grow(int minCapacity) {
        //old容量爲元素數組的元素個數
        int oldCapacity = elementData.length;
        // 擴容至 old + (old/2)
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //超過最大容量值,擴容至Integer的最大值    
        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);
    }

    //大容量,容量值小於0拋出異常,容量值大於最大數組長度返回Integer的最大值2^31-1
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

add(int index, E element)

//指定位置添加元素
public void add(int index, E element) {
        //邊界檢查
        rangeCheckForAdd(index);
        //擴容
        ensureCapacityInternal(size + 1); 
        
        //將指定位置後的元素複製到擴容後的元素集合中
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //指定位置賦值                 
        elementData[index] = element;
        size++;
    }

//邊界檢查
 private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }   

    /* @param      src      the source array.
     * @param      srcPos   starting position in the source array.
     * @param      dest     the destination array.
     * @param      destPos  starting position in the destination data.
     * @param      length   the number of array elements to be copied.
     */
    //將src的srcPos的連續length個元素複製到dest的destPos的連續位置(替換)
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

add 集合

public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //將集合元素追加到elementData後
        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)
            //先將指定位置後的元素copy移動後指定位置的元素
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
        //將空出來的元素用添加的集合參數賦值
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

上圖,更直觀一點:

remove 指定索引位置的元素

public E remove(int index) {
        //邊界檢查
        rangeCheck(index);
        //增長修改次數
        modCount++;
        //獲取指定索引位置的元素
        E oldValue = elementData(index);
        //須要移動的元素的數量
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //將index後的元素向前移動一位
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //size減1,最後一個元素賦值null,等待GC處理                     
        elementData[--size] = null; // clear to let GC do its work
        //返回刪除的元素
        return oldValue;
    }

remove指定的元素

//遍歷刪除指定的元素
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;
    }

//快速移除
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
    }

set

public E set(int index, E element) {
       //邊界檢查
        rangeCheck(index);
        
        E oldValue = elementData(index);
        //賦新值
        elementData[index] = element;
        return oldValue;
    }

get

public E get(int index) {
        //邊界檢查
        rangeCheck(index);
        //返回指定索引元素
        return elementData(index);
    }

indexOf

//遍歷獲取第一個元素的索引,找不到返回-1
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;
    }

contains

//索引大於0即爲包含指定元素
public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

trimToSize

將容量和實際數組元素個數保持一致,刪除擴容的null元素.

public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

retainAll

獲取兩個集合的交集

public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

private boolean batchRemove(Collection<?> c, boolean complement) {
        
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    //若是包含元素直接賦值
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            //若是循環異常中斷,正常狀況下r==size
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            //若是w!=size ,將w後的元素賦值爲null
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

iterator

迭代

public Iterator<E> iterator() {
        return new Itr();
    }


 private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
        
        //是否還有下一個元素
        public boolean hasNext() {
            return cursor != size;
        }
        
        //獲取下一個元素
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
        
        //刪除元素
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

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

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.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();
        }
    }

subList

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
    
    //邊界檢查
    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + 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;
        }

sort

只須要知道sort內部是歸併排序和Tim Sort便可.此處不展開詳細的排序算法.

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

    public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c) {
        if (c == null) {
            sort(a, fromIndex, toIndex);
        } else {
            rangeCheck(a.length, fromIndex, toIndex);
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, fromIndex, toIndex, c);
            else
                TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
        }
    }   
     
     // legacyMergeSort
     private static <T> void legacyMergeSort(T[] a, int fromIndex, int toIndex,
                                            Comparator<? super T> c) {
        T[] aux = copyOfRange(a, fromIndex, toIndex);
        if (c==null)
            mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
        else
            mergeSort(aux, a, fromIndex, toIndex, -fromIndex, c);
    }
     //mergeSort  歸併排序
     private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        int length = high - low;

        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low &&
                         ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }

        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off);
        mergeSort(dest, src, mid, high, -off);

        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }


    ///TimSort.sort
    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;

        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted

        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }

        /**
         * March over the array once, left to right, finding natural runs,
         * extending short natural runs to minRun elements, and merging runs
         * to maintain stack invariant.
         */
        TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
        int minRun = minRunLength(nRemaining);
        do {
            // Identify next run
            int runLen = countRunAndMakeAscending(a, lo, hi, c);

            // If run is short, extend to min(minRun, nRemaining)
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen, c);
                runLen = force;
            }

            // Push run onto pending-run stack, and maybe merge
            ts.pushRun(lo, runLen);
            ts.mergeCollapse();

            // Advance to find next run
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }

總結

  • 初始容量爲10
  • 動態擴容容量爲 old + old/2
  • 隨機查找效率較高
  • 插入和刪除慢,須要移動元素
  • 排序用歸併排序和TimSort
  • 動態刪除元素會拋出異常,可用迭代器實現
  • 線程不安全

參考

若有錯誤,歡迎批評指正,望不吝賜教!!!

相關文章
相關標籤/搜索