Java 的 ArrayList 的底層數據結構


1. 數據結構--ArrayList源碼摘要

ublic class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

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

    /**
     * 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 == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
}

ArrayList 的底層最重要的兩個屬性:Object 數組和 size 屬性。

 

2. ArrayList 的底層數組的調整

add方法--ArrayList源碼摘要

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

grow方法--ArrayList源碼摘要

 
         

/**

 
         

     * Increases the capacity of this <tt>ArrayList</tt> instance, if

 
         

     * necessary, to ensure that it can hold at least the number of elements

 
         

     * specified by the minimum capacity argument.

 
         

     *

 
         

     * @param   minCapacity   the desired minimum capacity

 
         

     */

 
         

    public void ensureCapacity(int minCapacity) {

 
         

        int minExpand = (elementData != EMPTY_ELEMENTDATA)

 
         

            // any size if real element table

 
         

            ? 0

 
         

            // larger than default for empty table. It's already supposed to be

 
         

            // at default size.

 
         

            : DEFAULT_CAPACITY;

 
         

 

 
         

        if (minCapacity > minExpand) {

 
         

            ensureExplicitCapacity(minCapacity);

 
         

        }

 
         

    }

 
         

 

 
         

    private void ensureCapacityInternal(int minCapacity) {

 
         

        if (elementData == EMPTY_ELEMENTDATA) {

 
         

            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

 
         

        }

 
         

 

 
         

        ensureExplicitCapacity(minCapacity);

 
         

    }

 
         

 

 
         

    private void ensureExplicitCapacity(int minCapacity) {

 
         

        modCount++;

 
         

 

 
         

        // overflow-conscious code

 
         

        if (minCapacity - elementData.length > 0)

 
         

            grow(minCapacity);

 
         

    }

 
         

 

 
         

    /**

 
         

     * The maximum size of array to allocate.

 
         

     * Some VMs reserve some header words in an array.

 
         

     * Attempts to allocate larger arrays may result in

 
         

     * OutOfMemoryError: Requested array size exceeds VM limit

 
         

     */

 
         

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

 
         

 

 
         

    /**

 
         

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

 
         

    }

 
         

 

 
         

    private static int hugeCapacity(int minCapacity) {

 
         

        if (minCapacity < 0) // overflow

 
         

            throw new OutOfMemoryError();

 
         

        return (minCapacity > MAX_ARRAY_SIZE) ?

 
         

            Integer.MAX_VALUE :

 
         

            MAX_ARRAY_SIZE;

 
         

    }

 

 

2點結論:

  a. ArrayList 是通過將底層 Object 數組復制的方式(System.arraycopy方法)來處理數組的增長;

  b. 當ArrayList 的容量不足時,其擴充容量的方式:先將容量擴充至當前容量的1.5倍,若還不夠,則將容量擴充至當前需要的數量(grow方法)。

 

 

remove 方法--ArrayList源碼摘要

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

  

/**

 
         

     * 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&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;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;

 
         

    }

 
         

 

 
         

    /*

 
         

     * Private remove method that skips bounds checking and does not

 
         

     * return the value removed.

 
         

     */

 
         

    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

 
         

    }

 

這也就解釋了 ArrayList 的特點:增加、刪除和移動元素的效率低(數組復制過程消耗資源較多); 而查找元素和更新元素的效率高。

3. ArrayList與Vector的區別

 

1) vector 是線程同步的,所以它也是線程安全的,而arraylist 是線程異步的,是不安全的。如果不考慮到線程的安全因素,一般用 arraylist效率比較高。

 

2)如果集合中的元素的數目大於目前集合數組的長度時,vector 增長率為目前數組長度的100%, 而arraylist 增長率為目前數組長度的50% .如果在集合中使用數據量比較大的數據,用vector有一定的優勢。

 

3)如果查找一個指定位置的數據,vector和arraylist使用的時間是相同的,都是O(1) ,這個時候使用vector和arraylist都可以。

 

而如果移動一個指定位置的數據花費的時間為O(n-i)n為總長度,這個時候就應該考慮到使用linklist ,因為它移動一個指定位置的數據所花費的時間為0(1),而查詢一個指定位置的數據時花費的時間為0(i)。


免責聲明!

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



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