【Java】集合源碼 - ArrayList

1. 概述

ArrayList 是 List 的一個實現, 其底層使用數組來存儲元素, 且支持數組動態擴容

因爲底層使用數組, 所以 ArrayList 的查找較快, 增加和刪除較慢

2. 類

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable 
        {
     	   ......
        }

繼承實現關係圖

在這裏插入圖片描述

  1. 繼承 AbstractList 抽象類
    說明其是一個List,且實現了List相關的方法。

  2. 實現 RandomAccess 接口
    RandomAccess 是一個標記接口, 用來標記支持快速訪問的類。RandomAccess 接口主要用在 Collections 工具類上, Collections 中的一些靜態方法在操作集合遍歷元素時, 如果該集合實現了RandomAccess 接口則使用 for 循環遍歷, 如果沒有則使用迭代器遍歷

  3. 實現 Clone 接口
    實現了clone()方法。

  4. 實現 Serializable 接口
    標記接口,表明 ArrayList 可以被序列化,並且加入了 serialVersionUID 字段標識版本。

  5. 實現 List 接口
    AbstractList 已經實現了 List 接口,此處卻又實現了一次是因爲,如果不這樣做的話在使用代理時會報異常,具體參考附錄鏈接。

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; 
    // 集合的元素個數, 不一定等於elementData.length
    private int size;

4. 方法

構造方法

構造方法共有三個

  1. 無參構造
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

該方法將默認的空數組賦給 elementData , 在調用 add 方法時會爲其分配一個默認長度爲10的數組。

  1. 可分配初始數組大小的有參構造
    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);
        }
    }
  1. 參數爲Collection集合的構造方法
  public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // toArray() 方法有可能不會返回Object[]對象
            if (elementData.getClass() != Object[].class)
            // 使用Native方法 Arrays.copy()
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 數組長度爲0,爲其分配空數組
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

add()

    public boolean add(E e) {
    	// 確保數組容量夠用, size是元素個數
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 將元素加在末尾
        elementData[size++] = e;
        return true;
    }

進入 ensureCapacityInternal (int minCapacity)

    private void ensureCapacityInternal(int minCapacity) {
        // 通過 calculateCapacity 計算容量, 默認容量則返回10, 否則返回minCapacity, 在當做參數傳遞, 
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

進入ensureExplicitCapacity(int minCapacity)

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

        // 需要的容量大於元素數組的長度則調用grow(minCapacity)方法進行擴容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

進入grow(int minCapacity)

  private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // 新容量爲舊容量的1.5倍, >> 向右移位運算, 相當於 / 2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新容量還是小於最小需要的容量
        if (newCapacity - minCapacity < 0)
        // 那麼直接將需要的容量作爲新容量
            newCapacity = minCapacity;
        // 如果新容量大於了數組大小的最大值, 那麼就將最大值賦給它
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 調用 Native 方法進行數組拷貝
        elementData = Arrays.copyOf(elementData, newCapacity);
        // 擴容完成, 返回add方法添加元素
    }

add(index)

    public void add(int index, E element) {
    	// 範圍檢查 index要在 0 到 size 之間, 否則拋出異常
        rangeCheckForAdd(index);
		// 同上add()方法一樣, 確保數組大小夠用, 不夠擴容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
     
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

remove()

public E remove(int index) {
		// 檢查index 在 0 到 size 之間
        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;
    }

remove(Object)

   public boolean remove(Object o) {
   		// 判空處理, 防止空指針異常
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    // index後的元素向前移動 同 remove(int)中的邏輯
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                // 對象的話調用equals()
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

removeAll()

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

retainAll()

    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

batchRemove()

removeAll() 和 retainAll() 的邏輯類似, 所以都同一由同一個方法batchRemove()實現, 只是在傳參的時候, removeAll() 傳 false表示不保留相同的元素, retainAll()傳true表示只保留相同的元素

 private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
        	// 遍歷 elementData
            for (; r < size; r++)
            	// complement 爲 false 表示 c 不包含的複製保存, c 包含的不復制 true 則相反
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // 如果 contains 方法拋異常, 則執行finally, r != size 表示還沒複製完成 
            if (r != size) {
            	// 繼續複製剩下的元素
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            // 發生了刪除, 元素數量變少, 需要把末尾的重複對象引用釋放掉, 以便GC回收
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                // 更新size大小
                size = w;
                modified = true;
            }
        }
        return modified;
    }

indexOf()

    public int indexOf(Object o) {
    	// 判空, 防止equals空指針
        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;
    }

get(int)

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

        return elementData(index);
    }

set(int, E)

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

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

elementData(int)

    E elementData(int index) {
        return (E) elementData[index];
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章