ArrayList 學習筆記

在這裏插入圖片描述

ArrayList

java中的數組,在創建的時候需要指定數組的長度。一旦創建後,數組的大小就固定了,不能夠再變化。
但實際開發過程中,經常需要根據保存對象數量的增加擴大範圍。ArrayList 就是數組可調整大小的實現,它允許添加所有元素,也就是說,可以往數組裏面添加 null 元素。

properties

先看看所有屬性:

public 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 = {};

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

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

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

可以看到,在JDK8當中,數組的默認初始化容量是10:

private static final int DEFAULT_CAPACITY = 10;

而且它有兩個空數組實例。

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

它們都是空數組,區別在於,往裏面添加第一個元素的時候,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 會以默認的初始化容量初始化elementData的大小,而EMPTY_ELEMENTDATA 會以給定的容量初始化大小,是這樣子嗎?我們看看代碼。

Constractor ArrayList()

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

可以看到,初始化的時候,就只是把一個static類型的空數組分給elementData.並沒有分配大小。它的大小是0.

    public static void main(String args[]) {
        List list = new ArrayList();
        System.out.println(list.size());
    }

輸出內容:

0

Process finished with exit code 0

Constractor ArrayList(int initialCapacity)

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

可以看到,一旦給定了初始化容量的大小,就會根據給定的值創建對應長度的Object數組。
可以寫一個測試類測試一下。

    public static void main(String args[]) {
        ArrayList arrayList2 = new ArrayList(20);
        System.out.println("ArrayList.size() == " + arrayList2.size());
        arrayList2.add(null);
        arrayList2.add(null);
        arrayList2.add(null);
        arrayList2.add(null);
        System.out.println("ArrayList.size() == " + arrayList2.size());
    }

可以看到輸出內容是:

ArrayList.size() == 0
ArrayList.size() == 4

Process finished with exit code 0

用一個給定的值構造ArrayList時。會根據這個值初始化ArrayList的成員變量。 this.elementData = new Object[initialCapacity]; 這個時候它的size沒有任何變化,還是0. 但是添加一個元素的時候。add方法會對size進行加一操作。代碼: elementData[size++] = e;

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

可以看一下ArrayList的容量擴充機制。比如現在的size是20. 添加一個元素後,他的最小容量是21. 也就是minCapacity的值是21.如果這個時候 elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA空數組。就取minCapacity和DEFAULT_CAPACITY的最大值。然後再拿這個值和當前elementData的長度比較,如果確實要增加,新的容量,是原來的容量,向右位移一位後的值加上原來的值。大致上就是以0.5倍的大小進行增長。int newCapacity = oldCapacity + (oldCapacity >> 1); 如果容量超過了最大值:MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 就取最大的值MAX_ARRAY_SIZE = Integer.MAX_VALUE。MAX_VALUE 的最大值是 0x7fffffff;

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_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);
    }
   
       /**
     * 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;
    }

通過數組下標訪問元素

可以看到,根據index獲取元素的時候,會先檢查index的範圍,index必須小於size.否則拋IndexOutOfBoundsException。 然後根據index 直接訪問elementData的元素。

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    /**
     * Checks if the given index is in range.  If not, throws an appropriate
     * runtime exception.  This method does *not* check if the index is
     * negative: It is always used immediately prior to an array access,
     * which throws an ArrayIndexOutOfBoundsException if index is negative.
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

equals(Object o)方法

判斷邏輯是:

  1. 先判斷是否是同一個對象 o == this. 如果是true,就返回true.
  2. 判斷給的對象是不是 List 的實例,如果不是,就返回false.
  3. 獲取 this 和給定對象的迭代器 listIterator(). 同時遍歷,判斷相同的位置上,所包含的元素是否相等。用 equals方法判斷。
  4. 在判斷長度是不是相等,如果不等,返回false.如果相等,就返回true.
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

hashCode() 方法

ArrayList的hashCode()方法邏輯:

  • 初始化hashCode的值爲1
  • 遍歷包含的每一個元素,將當前hashCode的值乘以31. 加上下一個元素的hashCode值.下一個元素爲null,那麼它的hashCode值爲0.
    /**
     * Returns the hash code value for this list.
     *
     * <p>This implementation uses exactly the code that is used to define the
     * list hash function in the documentation for the {@link List#hashCode}
     * method.
     *
     * @return the hash code value for this list
     */
    public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

漂亮的移除中間片段

加入一個數組,只取中間一段,它的實現方法是:

  • 用 listIterator(fromIndex) 找到開始的地方。
  • 跳過要保留的長度。
  • 遍歷剩下的元素,先移到下一個位置,再移除上一個位置的元素。
      /**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
     * (If {@code toIndex==fromIndex}, this operation has no effect.)
     *
     * <p>This method is called by the {@code clear} operation on this list
     * and its subLists.  Overriding this method to take advantage of
     * the internals of the list implementation can <i>substantially</i>
     * improve the performance of the {@code clear} operation on this list
     * and its subLists.
     *
     * <p>This implementation gets a list iterator positioned before
     * {@code fromIndex}, and repeatedly calls {@code ListIterator.next}
     * followed by {@code ListIterator.remove} until the entire range has
     * been removed.  <b>Note: if {@code ListIterator.remove} requires linear
     * time, this implementation requires quadratic time.</b>
     *
     * @param fromIndex index of first element to be removed
     * @param toIndex index after last element to be removed
     */
   protected void removeRange(int fromIndex, int toIndex) {
        ListIterator<E> it = listIterator(fromIndex);
        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
            it.next();
            it.remove();
        }
    }
發佈了76 篇原創文章 · 獲贊 9 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章