1. 程式人生 > >Android常用集合List之實現類ArrayList解析

Android常用集合List之實現類ArrayList解析

前言

基於Java1.8原始碼,層級結構就不再多敘述,只介紹幾個內部方法解析

主要屬性

    /**
     * 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.
     *
     * Package private to allow access from java.util.Collections.
     */
transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;

解析:EMPTY_ELEMENTDATA 屬性,從註釋上看就是提供一個預設的空的陣列;elementData 一個數組,裡面存放的集合裡面的所有資料 size 不做敘述了,大家都懂。

構造方法解析

//構造方法1 傳入初始化 elementData容量,並建立一個容量為initialCapacity大小的陣列
public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; }
    /** 構造方法2 預設構造一個容量為10的陣列
     * Constructs an empty list with an initial capacity of ten.
     */
public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
    //構造方法3 傳入一個Collection的實現類(很多,List Set都是)就做了2步操作 資料的賦值和一步校驗,校驗程式碼可以去看Arrays這個類
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

幾個主要的和常用的方法

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
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;
    }

解析: 通過indexOf方法找到物件下標位置(如果泛型不去重寫equals方法,就按照預設的實現處理,具體應用中,equals方法經常會被重寫,達到需要的判定物件是否是一個的標準),通過返回值判定是否有這個物件 注意實現,採用迴圈遍歷,在實際應用中,一定要考慮資料量的問題

/**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

解析:通過下標去替換某個資料,並返回原來的物件。如原始碼所示,不難理解.


下面是(個人認為)是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;

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

解析:如 以上程式碼所示,新增一個數據 ensureCapacityInternal 字面解釋就是確保內部儲存的容量是正確的。看看他做了些事情: 1、先看傳入值是 size+1 此刻還沒有對size進行加操作,就可以認為這是一個預容器容量 2、如果是預設構造產生的儲存物件 就比較兩值取最大值 3、如果不是預設構造物件 這個時候就要明確內部儲存的容量大小了 就是 ensureExplicitCapacity 這個方法 首先modCount++ 這個是幹嘛的?稍後簡單說下。 然後校驗陣列長度是否足夠,如果不夠,進行擴容操作。這個擴容操作簡單說下吧,第一步,進行原長度1.5倍擴容;第二部,校驗這個1.5倍的擴容值是否能夠滿足預需要長度,如不足進行替換;第三步,校驗陣列最大容量為,程式碼很明顯一看就知道;最後拷貝新陣列。
最後一步進行size++替換

remove方法

 public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) 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;
    }

解析:通過下標刪除某個資料。實現原理,通過系統方法複製。該方法含義,將目標陣列a從index+1位置開始的資料複製到陣列b的index以後。第一個陣列為陣列a,第二個陣列為陣列b 最後一個引數是需要拷貝的個數,通過這個方法就實現了資料從陣列中移除出去,最後size要執行–操作

新增一個Collection

public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

解析:同樣需要確保陣列容量的正確性,程式碼上文已解釋,然後處理這部分新增的資料。方法如同remove的複製方法,不再解釋

public boolean addAll(int index, Collection<? extends E> c) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

解析 如程式碼所示,新增到指定位置。有兩部主要操作,第一部分,將原本index以後的資料插入到index+numNew之後,第二部,將Collection的所有資料插入到陣列的index之後

modCount++

這個操作伴隨著整個ArrayList的增刪操作都會加一。具體在那裡用到了?List介面有個方法是iterator(),就是迭代器這一步操作.
首先看看ArrayList的最簡單實現(為啥是最簡單的,因為有的部分操作目前還沒有時間整理,整個原始碼部分還有一點其他的模組沒有說到)

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

再看看Itr的原始碼

/**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        // The "limit" of this iterator. This is the size of the list at the time the
        // iterator was created. Adding & removing elements will invalidate the iteration
        // anyway (and cause next() to throw) so saving this value will guarantee that the
        // value of hasNext() remains stable and won't flap between true and false when elements
        // are added and removed from the list.
        protected int limit = ArrayList.this.size;

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

        @SuppressWarnings("unchecked")
        public E next() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            int i = cursor;
            if (i >= limit)
                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();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
                limit--;
            } 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;

            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

解析:這是ArrayList的一個內部類,在初始化的時候modCount就賦值給expectedModCount,這一步操縱是為了防止在初始化一個新的迭代器之後依舊有增刪操作,那麼就會直接影響迭代器的 方法,通過這個modCount去判定在使用迭代器的時候是否是最新的準確的資料,每次有資料操作之後再次使用迭代器,應當重新初始化。