Java 集合之ArrayList

麥克斯維發表於2019-01-10

1.回顧

在上一篇 我們介紹了java集合的整體架構,為了複習方便請重新看下圖

Java 集合之ArrayList

Colletion 有List Set等子介面 而每個子介面又有具體的實現類,本文要講的ArrayList就是List的一種實現。List可儲存有序可重複的元素,那麼可知ArrayList也是。相反Set卻是無序不可重複的。

2.結構

那麼我們開始重點看ArrayList,通過idea可得下圖

Java 集合之ArrayList

由上圖知

實現RandomAccess介面:可以通過下標序號快速訪問

實現了Cloneable,能被克隆

實現了Serializable,支援序列化

再看類中方法如下

Java 集合之ArrayList

可見ArrayList除了實現類Collection介面方法外 還多了許多過載多方法 例如

void add(int index, E element)
boolean addAll(int index, Collection<? extends E> c)
E get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
E remove(int index)
E set(int index, E element)
List<E> subList(int fromIndex, int toIndex)
複製程式碼

上面的方法 顧名思義都可以猜出來 由於ArrayList是有序可重複的,其中維護了一個索引index 固可以以指定索引index來進行更多的操作,例如獲取指定索引位置的元素,刪除指定索引位置的元素,擷取某個索引範圍的所有元素等等

3.原始碼分析

一.成員變數

private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
private int size;
複製程式碼
  • DEFAULT_CAPACITY預設容量是10
  • EMPTY_ELEMENTDATA 陣列
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  • elementData 存放元素
  • size 元素個數

二.建構函式

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);
        }
    }
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
複製程式碼

有三個建構函式 分析程式碼可知可初始化時傳遞一個int值指定容量,也可以不傳預設10的容量 還可以傳遞一個指定Collection 來初始化 其中第三個指定Collection來初始化較長,無非就是先把集合轉化成陣列 然後把元素複製到list中去來完成初始化

三.常用方法

新增

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

其中呼叫 ensureCapacityInternal 方法

 private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
複製程式碼

再呼叫 ensureExplicitCapacity 方法

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
複製程式碼

又呼叫 grow 方法

     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);
    }
複製程式碼

總結以上便是新增前需要確定容量,噹噹前陣列容量不夠時就需要擴容,然後調Arrays.copyOf()建立一個新的陣列並將資料拷貝到新陣列中,且把引用賦值給elementData

刪除remove

 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;
    }
    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;
    }
複製程式碼

兩種過載方法 先檢查範圍 然後拿到被刪的元素 然後把被刪元素後到元素往前移動 複製到目標陣列 然後返回被刪元素

修改set

public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
複製程式碼

這段程式碼比較簡單 範圍檢查和重新賦值

獲取get

public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
複製程式碼

這個估計都不用我說把

4.總結

通過結構原始碼都分析我們知道,ArrayList 底層就是通過陣列來實現,新增的時候檢查容量不夠就擴容,然後賦值 刪除時則把被刪元素後往前移動 更新則比較簡潔明白。

Java 集合之ArrayList

相關文章