ArrayList源碼分析筆記


ArrayList源碼分析筆記

先貼出ArrayList一些屬性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	/**
     * 系列化ID.
     */
    private static final long serialVersionUID = 8683452581122892189L;
    /**
     * 默認初始化容量.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用於空實例的共享空數組實例.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 共享的空數組實例,用於默認大小的空實例。我們將此與EMPTY_ELEMENTDATA區別開來,
       以了解添加第一個元素時需要膨脹多少。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存儲ArrayList的元素的數組緩沖區。 ArrayList的容量是此數組緩沖區的長度。添加第
       一個元素時,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空
       ArrayList都將擴展為DEFAULT_CAPACITY。
     */
    transient Object[] elementData; 

    /**
     * ArrayList的大小(它包含的元素數).
     *
     * @serial
     */
    private int size;

以上屬性注釋都已經被翻譯成中文,通過這些注釋,我們大概能了解到的一些有價值的信息

  • ArrayList底層數據結構是一個Object數組

  • ArrayList的默認初始化容量為10

  • 一個空的ArrayList集合在添加第一個元素時被擴容為10

  • ArrayList的大小是通過一個名為size的int變量存儲的

源碼繼續往下看,先看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);
        }
    }

    /**
     * 通過這里可以看出,當調用ArrayList的無參構造函數時,這個ArrayList的Object數組被初始化為			   DEFAULTCAPACITY_EMPTY_ELEMENTDATA=10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    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一共有三個構造函數,一個無參構造,兩個有參構造。

當我們在創建一個Arraylist集合時,如果不指定容量,也就是調用無參構造函數,那么這個Arraylist會被初始化為10.

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

另外注意到另外兩個有參構造,一個參數為int initialCapacity,也就是我們指定的初始化容量,這里對這個initialCapacity進行了一些簡單的驗證

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {//initialCapacity > 0
            this.elementData = new Object[initialCapacity];//將一個容量為initialCapacity的新Object數組賦給elementData。
        } else if (initialCapacity == 0) {//initialCapacity==0
            this.elementData = EMPTY_ELEMENTDATA;//將EMPTY_ELEMENTDATA這個空object賦給elementData
        } else {//其他情況,也就是小於0,直接拋異常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

另一個有參構造

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//將Collection集合轉化為數組,然后賦給elementData這個object數組
        if ((size = elementData.length) != 0) {//如果這個集合長度不為0
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)//如果集合轉化后的數組不是Object數組
                elementData = Arrays.copyOf(elementData, size, Object[].class);//轉化為Object數組
        } else {//其他情況,也就是elementData的長度為0嘛
            // 替換為EMPTY_ELEMENTDATA空的數組
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

可以看到這個構造函數,它是直接把一個Collection丟了進去,這也就是說,在new一個ArrayList時我們可以把一個Collection集合放到參數列表中。

接下來再來看ArrayList的add方法

 /**
     * 將元素添加到列表的末尾
     *
     * @param e 要被追加到list中的元素
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;//將元素e添加到數組中,下標為size++,其實也就是size,然后添加后size+1,
        return true;
    }

這里就兩行代碼,調用ensureCapacityInternal這個方法,並且參數為size+1。點進去看下ensureCapacityInternal()這個方法

/*
	通過這個方法我們可以看出,當我們第一次調用add方法的時候,elementData數組的size為0,那么size+1就為1,所以minCapacity也為1,這里先通過一個if判斷,判斷minCapacity是不是一個空的object數組,如果是的話,minCapacity就取DEFAULT_CAPACITY和minCapacity的最大值,也就是10嘛
*/
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    //判斷完之后進入了這個方法,同時參數為minCapacity,如果是第一次調用add方法的話參數為minCapacity就是默認大小10,不是第一次調用的話,就是size+1,
        ensureExplicitCapacity(minCapacity);
    }


    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
//這個grow()就是擴容函數,現在minCapacity的話,還是一樣的分析,如果第一次調用add方法的話就是10,同時elementData.length為0,不是第一次調用的話minCapacity就是size+1,elementData.length也就是數組的長度為size
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

讀完這一段源碼,可以得出以下結論

  • 當我們創建ArrayList時,如果不指定ArrayList的大小,那么第一次調用add方法時,底層會調用擴容方法grow()對Arraylist進行擴容
  • 當ArrayList里面已經存滿了元素的時候,再調用add方法,此時會底層會調用grow方法進行擴容,比如ArrayList的大小為10,里面也已經有10個元素了,當我們再調用add方法向里面添加元素的時候,此時Arraylist會觸發擴容。

接下來我們再來看下grow方法,了解一些Arraylist到底是怎么樣擴容的

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
    	//如果數組當前長度+數組當前長度/2 < 數組當前元素個數+1
        if (newCapacity - minCapacity < 0)
         //就把數組當前元素個數+1賦給newCapacity
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 最后我們可以看到,這里進行了一個復制操作,將elementData里面的元素,復制到一個新的長度為newCapacity的Object數組,然后再把它賦給elementData
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

這段代碼應該很清晰吧,grow方法傳進來的參數就是上面minCapacity,第一次調用add方法時是10,其他時候調用時是size+1(當前存儲元素個數+1)

這里簡單來說就是將elementData當前長度給oldCapacity,然后newCapacity = oldCapacity + oldCapacity >> 1(左移操作,相當於除於2),如果oldCapacity = 10的話,newCapacity = 10 +(10/2),也就是15。通過以上代碼,不難看出,ArrayList的擴容方法實際上就是將底層Object數組復制到了一個新的長度更長的數組里面去,而這個新數組的長度是原來的數組長度+原來數組長度的二分之一,其實就可以說了擴容了1.5倍。

通過對以上源碼進行分析,我們可以得出以下結論了

總結:

  • ArrayList底層數據數據結構是一個Object類型的數組
  • ArrayList的默認初始化容量為10
  • 當我們在創建ArrsyList時不指定ArrsyList初始大小,第一次調用add方法時擴容為初始化大小10
  • ArrayList的大小是通過一個名為size的int變量存儲的
  • ArrayList有一個構造函數允許Collection集合作為參數,並且會將這個Collection集合轉化為數組
  • ArrayList在添加第elementData.length+1個元素時會發生擴容,比如數組長度為10,在添加第11個元素時擴容
  • ArrayList擴容大小為原來的1.5倍,底層實現是通過原來數組長度+原來數組長度左移1位完成,而不是直接通過乘法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM