簡(jiǎn)單聊聊 ArrayList

哈嘍,大家好,今天來(lái)聊一聊我們?cè)贏ndroid開(kāi)發(fā)中經(jīng)常用的到一個(gè)類,ArrayList

ArrayList是以數(shù)組為底層實(shí)現(xiàn)的,并且可以動(dòng)態(tài)的加減容量。但是要注意它不是線程安全的。


在分析源碼之前,我們先來(lái)了解兩個(gè)相關(guān)的知識(shí)點(diǎn),這樣更加有助于我們理解源碼。

Arrays.copyof

這個(gè)方法是用于復(fù)制數(shù)組的方法

    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

我們可以看到這里是傳入了兩個(gè)參數(shù),第一個(gè)參數(shù)是要復(fù)制的數(shù)組,第二個(gè)參數(shù)是復(fù)制后數(shù)組的長(zhǎng)度,如果傳入長(zhǎng)度和original的長(zhǎng)度一樣,那么就會(huì)復(fù)制original里面所有的數(shù)據(jù)。如果比original的長(zhǎng)度短,那么久復(fù)制到相應(yīng)長(zhǎng)度即可,如果比original的長(zhǎng)度還要長(zhǎng),那么就會(huì)用null或者0來(lái)填充。

System arraycopy

我們繼續(xù)跟進(jìn)上面Arrays.copyof的源碼我們會(huì)發(fā)現(xiàn):

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
        return copy;
    }

    @FastNative
    public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

我們發(fā)現(xiàn)Arrays.copyof最終調(diào)用的就是System.arraycopy方法,這是一個(gè)native方法,參數(shù)解釋如下:

  • src:要復(fù)制的數(shù)組。
  • srcPos:復(fù)制的起點(diǎn)。
  • dest:復(fù)制的目標(biāo)數(shù)組。
  • destPos:目標(biāo)數(shù)組復(fù)制的起點(diǎn)
  • length:要復(fù)制的值的個(gè)數(shù)
    要注意的是這里復(fù)制過(guò)后修改復(fù)制后的新數(shù)組并不會(huì)影響原來(lái)的數(shù)組的值。

接下來(lái)我們進(jìn)入正題,開(kāi)始聊聊ArrayList。

我們先來(lái)看看ArrayList實(shí)現(xiàn)了哪些接口

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • ArrayList實(shí)現(xiàn)了List這個(gè)接口,在List里我們可以看到有add,get,set等方法。
  • RandomAccess是一個(gè)標(biāo)志接口,實(shí)現(xiàn)RandomAccess這個(gè)接口后ArrayList支持快速隨機(jī)訪問(wèn)。
  • 實(shí)現(xiàn)Serializable后ArrayList支持序列化。

接下來(lái)我們來(lái)看看類里面的方法:

ArrayList(int initialCapacity)

    private static final Object[] EMPTY_ELEMENTDATA = {};

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

這個(gè)構(gòu)造函數(shù)傳入的initialCapacity為初始化ArrayList的長(zhǎng)度。如果長(zhǎng)度大于0,則new一個(gè)長(zhǎng)度為initialCapacity的數(shù)組并賦值給elementData。如果長(zhǎng)度為0,這直接將默認(rèn)空數(shù)組賦值給elementData。如果長(zhǎng)度小于0,則報(bào)錯(cuò)。

ArrayList()

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

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

這個(gè)無(wú)參構(gòu)造函數(shù)直接是將一個(gè)空數(shù)組賦值給elementData。

ArrayList(Collection<? extends E> c)

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

這個(gè)構(gòu)造函數(shù)傳入了一個(gè)集合,先將傳入的參數(shù)轉(zhuǎn)換為數(shù)組并賦值給elementData。再進(jìn)行elementData數(shù)組的長(zhǎng)度判斷,如果長(zhǎng)度大于0,將elementData數(shù)組轉(zhuǎn)換為一個(gè)Object的數(shù)組。如果elementData長(zhǎng)度為0,則直接賦值一個(gè)空數(shù)組。

indexOf(Object o)

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

這里先判斷傳入?yún)?shù)是否為null,因?yàn)锳rrayList是可以添加null的,所以我們要先判斷一下,利用for循環(huán)來(lái)一個(gè)一個(gè)對(duì)比,如果為null,則返回下標(biāo)。如果不為null,也是在for循環(huán)中做值的對(duì)比,注意這里用的equals來(lái)對(duì)比的值而不是用的==,找到后返回下標(biāo),如果沒(méi)有則返回-1。

get(int index)

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

        return (E) elementData[index];
    }

get方法是獲取對(duì)應(yīng)下標(biāo)的值,這里先判斷傳入的參數(shù)是否越界,如果沒(méi)有直接從數(shù)組中取出對(duì)應(yīng)的值,并返回。

set(int index, E element)

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

set方法是修改對(duì)應(yīng)下標(biāo)的值,這里先判斷傳入的index是否越界,如果沒(méi)有我們獲取現(xiàn)在下標(biāo)對(duì)應(yīng)的值,然后賦上新的值,并將原來(lái)的值返回。

add(E e)

    public boolean add(E e) {
        //這里先判斷是否需要增加數(shù)組的容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //賦值到最后一位。
        elementData[size++] = e;
        return true;
    }

    private static final int DEFAULT_CAPACITY = 10;

    private void ensureCapacityInternal(int minCapacity) {
        //如果數(shù)組在初始化的時(shí)候沒(méi)有傳入數(shù)組大小,我們先設(shè)置一個(gè)最小值。
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        //操作次數(shù)加1
        modCount++;

        //如果數(shù)組容量不夠,需要增容。
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

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

    private void grow(int minCapacity) {
        //獲取現(xiàn)在數(shù)組的大小。
        int oldCapacity = elementData.length;
        //獲取現(xiàn)在大小1.5倍的值。
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果還是不夠大,那么就直接讓數(shù)組大小為minCapacity。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果超過(guò)了規(guī)定的最大值,將用傳入的最小值來(lái)重新計(jì)算數(shù)組的大小。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //最后賦值所有值到新的數(shù)組。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        //如果符合條件的最小值大于規(guī)定的最大值,那么將值修改為Integer.MAX_VALUE,否則為規(guī)定的最大值。
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

關(guān)鍵解釋已經(jīng)注釋在代碼中。

add(int index, E element)

    public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

這個(gè)add方法和上一個(gè)add方法大致相同,在ensureCapacityInternal(size + 1)方法后調(diào)用System.arraycopy方法將數(shù)組從index下標(biāo)位置全部向后移動(dòng)一位,然后直接將index下標(biāo)位置賦上新傳進(jìn)來(lái)的值。

remove(int index)

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

在remove(int index)方法中我們先判斷傳入的index是否越界,如果沒(méi)有我們將index+1位置開(kāi)始的所有值向前移動(dòng)一位,這樣就覆蓋了index的值,然后將最后多出來(lái)的一位賦值為null,方便GC處理。

remove(Object o)

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

remove(Object o)方法我們需要先判斷傳入的Object是否為null,然后找到Object值的下標(biāo),因?yàn)檫@里已經(jīng)找到了下標(biāo),所以肯定沒(méi)有越界,然后直接調(diào)用fastRemove方法來(lái)刪除。fastRemove方法和上面的remove方法類似,這里也不再解釋了。

addAll(Collection<? extends E> c)

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

這里先通過(guò)ensureCapacityInternal(size + numNew)方法來(lái)判斷是否需要增容,然后直接調(diào)用System.arraycopy方法將新的數(shù)組賦值到elementData中。

addAll(int index, Collection<? extends E> c)

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

addAll(int index, Collection<? extends E> c)方法先判斷了傳入的index是否越界,如果沒(méi)有越界我們繼續(xù)判斷數(shù)組是否需要增容。然后這里有一個(gè)判斷,如果numMoved 的值大于0,就說(shuō)明index在數(shù)組的內(nèi),如果小于等于0,說(shuō)明index在數(shù)組最后一位或者在數(shù)組外,根據(jù)不同,分別調(diào)用System.arraycopy來(lái)進(jìn)行賦值。

removeAll(Collection<?> c)

    public boolean removeAll(Collection<?> c) {
        //先判斷c是否為null。
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            //這里傳入的complement為false,那么for中就是將不刪除的是有值全部放到最前面去。
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            //如果r不等于size,那么說(shuō)明上面的for循環(huán)報(bào)錯(cuò)了,這里將從報(bào)錯(cuò)開(kāi)始的所有值賦值到報(bào)錯(cuò)前調(diào)整過(guò)位置
            //的不刪除的值的后面。(這里有點(diǎn)沒(méi)說(shuō)清楚,大家看代碼應(yīng)該能懂 :))
            if (r != size) {
                System.arraycopy(elementData, r, elementData, w, size - r);
                w += size - r;
            }
            //w不等于size證明removeAll傳入的c不為null。這里從w開(kāi)始,將后面的所有值(上面for循環(huán)將不刪除
            //的值全部放到了w前面)賦值為null。
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

removeAll(Collection<?> c)方法的主要解釋都放在了代碼里面,這個(gè)有點(diǎn)繞,大家多看看代碼理解下應(yīng)該沒(méi)什么問(wèn)題。

ArrayList的主要方法這里就分析完成了,我們可以發(fā)現(xiàn)ArrayList在刪除,添加的時(shí)候會(huì)比較麻煩,在查詢的時(shí)候比較簡(jiǎn)單。所以以后再多查詢,少添加,少刪除的時(shí)候可以考慮ArrayList。

上文中如果有錯(cuò)誤的地方,歡迎大家指出,也歡迎大家留言交流。

3Q

?著作權(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)容