安卓ArrayList源碼解析

前話:本次解析基于JDK1.8.0,SDK26。

一、數(shù)據(jù)結(jié)構(gòu)

ArrayList 是Java提供的一個存儲數(shù)據(jù)的工具類,其內(nèi)部使用 一個Object[] (數(shù)組)來存儲我們的數(shù)據(jù)。后續(xù)的添加、刪除、查詢等操作實際上都是對數(shù)組進行操作。

transient Object[]elementData; //transient是序列化和反序列化的知識點

二、構(gòu)造方法

使用ArrayList的前提就是定義ArrayList對象,先來看一下ArrayList類中的幾個重要的成員變量:

//實際存儲數(shù)據(jù)的數(shù)組
transient Object[] elementData;
//數(shù)組中實際數(shù)據(jù)的個數(shù),注意并不是數(shù)組的長度(這塊要特別注意,后邊會用到)。默認為0
private int size;
//數(shù)組的默認容量或長度
private static final int DEFAULT_CAPACITY = 10;
//靜態(tài)的空數(shù)組
private static final Object[] EMPTY_ELEMENTDATA = {};
//靜態(tài)的默認的空數(shù)組,在后邊的構(gòu)造函數(shù)會看到和EMPTY_ELEMENTDATA的不同
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
1、首先分析最簡單的一種定義方式:

ArrayList list = new ArrayList();
其內(nèi)部實現(xiàn):

public ArrayList() {
        //直接把默認的靜態(tài)的空數(shù)組賦值給elementData
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
2、帶有初始化容量的構(gòu)造方法:

ArrayList list = new ArrayList(20);
其內(nèi)部實現(xiàn):

//指定創(chuàng)建一個長度為initialCapacity的數(shù)組
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //要求的長度>0,則直接創(chuàng)建一個該長度的數(shù)組
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //賦值為空數(shù)組
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //如果長度小于零,直接拋出異常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

三、add方法

對add方法的總結(jié)就是:當向ArrayList中添加一個元素時,先判斷當前數(shù)組中是否還有剩余空間,如果有剩余空間則直接將該元素添加至數(shù)組中元素的末尾,如果沒有剩余空間,先將數(shù)組擴容(擴容的算法簡單說就是將當前的數(shù)組容量擴大1.5倍),再將元素添加至數(shù)組中元素的末尾。

public boolean add(E e) {
        //每次add一個元素之前,先判斷size+1之后數(shù)組是否還有空余空間,
        //如果沒有剩余空間,對elementData進行擴容,如果有剩余空間則直接進行下一步。
        // 注意:size并不是數(shù)組的長度,而是數(shù)組中實際存儲對象的長度
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //注意size++,每次添加一個素,size增加1.
        elementData[size++] = e;
        return true;
    }

來重點看下ensureCapacityInternal方法:

private void ensureCapacityInternal(int minCapacity) {
        // elementData就是實際存儲數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu):Object[]
        // 如果當前數(shù)組為空數(shù)組
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //DEFAULT_CAPACITY 為 10,是默認數(shù)組的長度
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //繼續(xù)調(diào)用方法
        ensureExplicitCapacity(minCapacity);
    }

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //等價于:minCapacity > elementData.length
        if (minCapacity - elementData.length > 0)
            // 第一次add的時候,elementData.length=0,數(shù)組為空數(shù)組,所以肯定要把當前數(shù)組擴容
            // 如果是第N次add,此時minCapacity > elementData.length,也就是數(shù)組需要更大的空間
            //所以也需要擴容。
            grow(minCapacity);
    }
private void grow(int minCapacity) {
        //當前數(shù)組的長度(擴容之前的長度)
        int oldCapacity = elementData.length;
        //oldCapacity >> 1:位運算符,相當于oldCapacity/2,
        //新的容量等于:原來的容量 + 原來的容量的一半
        //所以每次擴容都是將數(shù)組的長度擴大為原來的1.5倍。
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        //如果新的擴大1.5倍后,數(shù)組的空間仍然不夠
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果需要的空間比 MAX_ARRAY_SIZE 還要大,
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //hugeCapacity:申請最大的空間
            newCapacity = hugeCapacity(minCapacity);

        //最后根據(jù)需要的空間和原來的數(shù)組中的數(shù)據(jù),將elementData重新賦值為擁有更大空間的數(shù)組
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

三、get方法

根據(jù)元素的坐標獲取元素,先判斷index是否合法,如果合法直接返回元素。

public E get(int index) {
        if (index >= size)
            //size是當前數(shù)組中元素的個數(shù),并不是數(shù)組的長度,如果查詢的index超過size,拋異常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        
        //直接返回index對應的元素
        return (E) elementData[index];
    }

四、set方法

set(int index, E element)方法是將原數(shù)組中的下標為index的元素替換為新的數(shù)據(jù)。

public E set(int index, E element) {
        if (index >= size)
            //老規(guī)矩,還是先判斷size的長度
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        
        //將老的元素替換為新的元素,并把老元素返回
        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

五、remove方法

public E remove(int index) {
        if (index >= size)
            //老規(guī)矩,先判斷size合法
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        //獲取要刪除的元素
        E oldValue = (E) elementData[index];

        //  假設一個數(shù)組有3個元素,現(xiàn)在刪除第二個元素(下標為1),numMoved= 3-1-1=1
        //numMoved表示在數(shù)組中刪除一個元素后,需要將該元素后邊的元素全部向前挪一位,那么一共要挪動的元素的個數(shù)即為numMoved
        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;
    }

六、indexOf 方法

indexOf 可以接受為Null的數(shù)據(jù),如果數(shù)據(jù)為null,則返回整個數(shù)組中第一個為null的元素的下標。如果不為null,則遍歷整個數(shù)組中的元素,找到相等的元素并返回下標。
需要注意的是:indexOf(E e) 方法的平均時間復雜度為O(n),因為它內(nèi)部是一個循環(huán),所以在使用indexOf方法的時候,盡量不要再一個for循環(huán)中再使用indexOf,因為這樣的時間復雜度為O(n*n),在內(nèi)存充足的情況下可以考慮使用“空間換時間”的方法,使用HashSet或者HashMap代替ArrayList。

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

七、size()方法

public int size() {
        //前邊已經(jīng)提到多次,ArrayList的size方法返回的是數(shù)組中元素的個數(shù),不是整個數(shù)組的長度。
        return size;
    }

總結(jié)

其實ArrayList內(nèi)部還有幾個操作方法,但是總體的思路都是針對于數(shù)組進行操作,另外,在寫代碼的時候一定要注意indexOf的使用,這句代碼的時間復雜度為O(n)。

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

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