ArrayList實(shí)現(xiàn)原理分析(Java源碼剖析)

  • ArrayList使用的存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)
  • ArrayList的初始化
  • ArrayList是如何動(dòng)態(tài)增長(zhǎng)
  • ArrayList如何實(shí)現(xiàn)元素的移除
  • ArrayList小結(jié)

ArrayList是我們經(jīng)常使用的一個(gè)數(shù)據(jù)結(jié)構(gòu),我們通常把其用作一個(gè)可變長(zhǎng)度的動(dòng)態(tài)數(shù)組使用,大部分時(shí)候,可以替代數(shù)組的作用,我們不用事先設(shè)定ArrayList的長(zhǎng)度,只需要往里不斷添加元素即可,ArrayList會(huì)動(dòng)態(tài)增加容量。ArrayList是作為L(zhǎng)ist接口的一個(gè)實(shí)現(xiàn)。
那么ArrayList背后使用的數(shù)據(jù)結(jié)構(gòu)是什么呢?
ArrayList是如何保證動(dòng)態(tài)增加容量,使得能夠正確添加元素的呢?

要回答上面的問(wèn)題,我們就需要對(duì)ArrayList的源碼進(jìn)行一番分析,深入了解其實(shí)現(xiàn)原理的話,我們就自然能夠解答上述問(wèn)題。

需要說(shuō)明的是,本文所分析的源碼引用自JDK 8版本

ArrayList使用的存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)

從源碼中我們可以發(fā)現(xiàn),ArrayList使用的存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)是Object的對(duì)象數(shù)組。
其實(shí)這也不能想象,我們知道ArrayList是支持隨機(jī)存取的類(lèi)似于數(shù)組,所以自然不可能是鏈表結(jié)構(gòu)。

/**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

我想大家一定對(duì)這里出現(xiàn)的transient關(guān)鍵字很疑惑,我們都知道ArrayList對(duì)象是可序列化的,但這里為什么要用transient關(guān)鍵字修飾它呢?查看源碼,我們發(fā)現(xiàn)ArrayList實(shí)現(xiàn)了自己的readObject和writeObject方法,所以這保證了ArrayList的可序列化。具體序列化的知識(shí)我們?cè)诖瞬贿^(guò)多贅述。有興趣的讀者可以參考筆者關(guān)于序列化的文章。

ArrayList的初始化

ArrayList提供了三個(gè)構(gòu)造函數(shù)。下面我們依次來(lái)分析

  • public ArrayList(int initialCapacity) 當(dāng)我們初始化的時(shí)候,給ArrayList指定一個(gè)初始化大小的時(shí)候,就會(huì)調(diào)用這個(gè)構(gòu)造方法。
List<String> myList = new ArrayList<String>(7);

源碼中這個(gè)方法的實(shí)現(xiàn)如下

/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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);
        }
    }

這里的EMPTY_ELEMENTDATA 實(shí)際上就是一個(gè)共享的空的Object數(shù)組對(duì)象。

/**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

上述代碼很容易理解,如果用戶(hù)指定的初始化容量大于0,就new一個(gè)相應(yīng)大小的數(shù)組,如果指定的大小為0,就復(fù)制為共享的那個(gè)空的Object數(shù)組對(duì)象。如果小于0,就直接拋出異常。

  • public ArrayList() 默認(rèn)的空的構(gòu)造函數(shù)。
    我們一般會(huì)這么使用
 myList = new ArrayList();

源碼中的實(shí)現(xiàn)是

/**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

其中DEFAULTCAPACITY_EMPTY_ELEMENTDATA 定義為

/**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

注釋中解釋的很清楚,就是說(shuō)剛初始化的時(shí)候,會(huì)是一個(gè)共享的類(lèi)變量,也就是一個(gè)Object空數(shù)組,當(dāng)?shù)谝淮蝍dd的時(shí)候,這個(gè)數(shù)組就會(huì)被初始化一個(gè)大小為10的數(shù)組。

  • public ArrayList(Collection<? extends E> c) 如果我們想要初始化一個(gè)list,這個(gè)list包含另外一個(gè)特定的collection的元素,那么我們就可以調(diào)用這個(gè)構(gòu)造函數(shù)。
    我們通常會(huì)這么使用
Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
ArrayList<Integer> list = new ArrayList<>(set);

源碼中是這么實(shí)現(xiàn)的

/**
     * 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;
        }
    }

首先調(diào)用給定的collection的toArray方法將其轉(zhuǎn)換成一個(gè)Array。
然后根據(jù)這個(gè)array的大小進(jìn)行判斷,如果不為0,就調(diào)用Arrays的copyOf的方法,復(fù)制到Object數(shù)組中,完成初始化,如果為0,就直接初始化為空的Object數(shù)組。

ArrayList是如何動(dòng)態(tài)增長(zhǎng)

當(dāng)我們像一個(gè)ArrayList中添加數(shù)組的時(shí)候,首先會(huì)先檢查數(shù)組中是不是有足夠的空間來(lái)存儲(chǔ)這個(gè)新添加的元素。如果有的話,那就什么都不用做,直接添加。如果空間不夠用了,那么就根據(jù)原始的容量增加原始容量的一半。
源碼中是如此實(shí)現(xiàn)的:

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal的實(shí)現(xiàn)如下:

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

        ensureExplicitCapacity(minCapacity);
    }

DEFAULT_CAPACITY為:

private static final int DEFAULT_CAPACITY = 10;

這也就實(shí)現(xiàn)了當(dāng)我們不指定初始化大小的時(shí)候,添加第一個(gè)元素的時(shí)候,數(shù)組會(huì)擴(kuò)容為10.

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

這個(gè)函數(shù)判斷是否需要擴(kuò)容,如果需要就調(diào)用grow方法擴(kuò)容

/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }

我們可以看到grow方法將數(shù)組擴(kuò)容為原數(shù)組的1.5倍,調(diào)用的是Arrays.copy
方法。
在jdk6及之前的版本中,采用的還不是右移的方法

int newCapacity = (oldCapacity * 3)/2 + 1;

現(xiàn)在已經(jīng)優(yōu)化成右移了。

ArrayList如何實(shí)現(xiàn)元素的移除

我們移除元素的時(shí)候,有兩種方法,一是指定下標(biāo),二是指定對(duì)象

list.remove(3);//index
list.remove("aaa");//object

下面先來(lái)分析第一種,也就是

  • public E remove(int index)
    源碼中是如此實(shí)現(xiàn)的
/**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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;
    }

對(duì)于數(shù)組的元素刪除算法我們應(yīng)該很熟悉,刪除一個(gè)數(shù)組元素,我們需要將這個(gè)元素后面的元素全部向前移動(dòng),并將size減1.
我們看到源碼中,首先檢查下標(biāo)是否在可用范圍內(nèi)。然后調(diào)用System.arrayCopy方法將右邊的數(shù)組向左移動(dòng),并且將size減一,并置為null。

  • public boolean remove(Object o)
    源碼中實(shí)現(xiàn)如下:
/**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    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;
    }

我們可以看到,這個(gè)remove方法會(huì)移除數(shù)組中第一個(gè)符合的給定對(duì)象,如果不存在就什么也不做,如果存在多個(gè)只移除第一個(gè)。
fastRemove方法如下

/**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    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;
    }

可以理解為簡(jiǎn)化版的remove(index)方法。

ArrayList小結(jié)

  • ArrayList是List接口的一個(gè)可變大小的數(shù)組的實(shí)現(xiàn)

  • ArrayList的內(nèi)部是使用一個(gè)Object對(duì)象數(shù)組來(lái)存儲(chǔ)元素的

  • 初始化ArrayList的時(shí)候,可以指定初始化容量的大小,如果不指定,就會(huì)使用默認(rèn)大小,為10

  • 當(dāng)添加一個(gè)新元素的時(shí)候,首先會(huì)檢查容量是否足夠添加這個(gè)元素,如果夠就直接添加,如果不夠就進(jìn)行擴(kuò)容,擴(kuò)容為原數(shù)組容量的1.5倍

  • 當(dāng)刪除一個(gè)元素的時(shí)候,會(huì)將數(shù)組右邊的元素全部左移

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容