Java集合之ArrayList源碼剖析

Java集合之ArrayList源碼剖析(jdk1.8)

1、簡介

ArrayList是一種以數組實現的List,與數組相比,它具有動態擴展的能力,因此也可稱之爲動態數組。

查詢快,增刪慢,線程不安全。

繼承圖譜如下:

ArrayList

ArrayList實現了List, RandomAccess, Cloneable, java.io.Serializable等接口。

ArrayList實現了List,提供了基礎的添加、刪除、遍歷等操作。

ArrayList實現了RandomAccess,提供了隨機訪問的能力。

ArrayList實現了Cloneable,可以被克隆。

ArrayList實現了Serializable,可以被序列化。

2、源碼分析

1、屬性

//用於序列化的id
private static final long serialVersionUID = 8683452581122892189L;

//默認構造方法的容量
private static final int DEFAULT_CAPACITY = 10;

//空數組,構造方法中參數爲0時使用--->new ArrayList(0)
private static final Object[] EMPTY_ELEMENTDATA = {};

//空數組,默認構造方法使用--->new ArrayList()
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//臨時存放元素的數組,使用transient修飾,不會被序列化
transient Object[] elementData; 

//元素實際個數
private int size;

//數組能分配的最大內存,超出會OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2、構造方法

ArrayList有三個構造方法。

//構造方法一,傳入容量
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() {
    	//當添加第一個元素時,擴容到默認大小10
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

   
//構造方法三,傳入一個集合,將其轉化爲ArrayList
public ArrayList(Collection<? extends E> c) {
    	//集合轉數組
        elementData = c.toArray();
    	
        if ((size = elementData.length) != 0) {
        	// 檢查c.toArray()返回的是不是Object[]類型,如果不是,重新拷貝成Object[].class類型 
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 如果c的空集合,則初始化爲空數組EMPTY_ELEMENTDATA
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

3、簡單方法

這部分方法有的是從高層繼承下來的,有的是對高層方法的實現,較爲簡單,部分如下:

public int size() {
        return size;
    }

public boolean isEmpty() {
        return size == 0;
    }

    
public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }


4、核心方法

這部分方法有些是對抽象List的實現,也有部分獨屬於ArrayList.

add(E e)方法

在ArrayList後面添加一個元素

public boolean add(E e) {
    // 檢查是否需要擴容
    ensureCapacityInternal(size + 1);
    // 把元素插入到最後一位
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

//計算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 如果是空數組DEFAULTCAPACITY_EMPTY_ELEMENTDATA,就初始化爲默認大小10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

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

    if (minCapacity - elementData.length > 0)
        // 擴容
        grow(minCapacity);
}

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 新容量爲舊容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新容量發現比需要的容量還小,則以需要的容量爲準
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果新容量已經超過最大容量了,則使用最大容量
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 以新容量拷貝出來一個新數組
    elementData = Arrays.copyOf(elementData, newCapacity);
}
add(int index, E element)方法

添加元素到指定位置,平均時間複雜度爲O(n)。

 public void add(int index, E element) {
     	//檢查是否越界
        rangeCheckForAdd(index);
		//檢查是否需要擴容
        ensureCapacityInternal(size + 1);  
        //將index及其後的元素向後移動一位,空出index的位置
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
     //空出的位置存放加入的元素
        elementData[index] = element;
     //數量加一
        size++;
    }
addAll(Collection<? extends E> c)方法

向ArrayList中加入集合,求並集

public boolean addAll(Collection<? extends E> c) {
    	//集合轉爲數組
        Object[] a = c.toArray();
    
        int numNew = a.length;
    	//檢查擴容
        ensureCapacityInternal(size + numNew);  
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
addAll(int index, Collection<? extends E> c)

從指定位置加入集合。

public boolean addAll(int index, Collection<? extends E> c) {
    	//檢查越界
        rangeCheckForAdd(index);

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

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

get(int index)方法

通過索引獲取元素,複雜度爲o(1)

public E get(int index) {
    // 檢查是否越界
    rangeCheck(index);
    // 返回數組index位置的元素
    return elementData(index);
}

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

E elementData(int index) {
    return (E) elementData[index];
}
remove(int index)方法

通過索引刪除元素,複雜度爲o(1)

public E remove(int index) {
    // 檢查是否越界
    rangeCheck(index);

    modCount++;
    // 獲取index位置的元素
    E oldValue = elementData(index);

    // 如果index不是最後一位,則將index之後的元素往前挪一位
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);

    // 將最後一個元素刪除,幫助GC
    elementData[--size] = null; // clear to let GC do its work

    // 返回舊值
    return oldValue;
}

remove(Object o)方法

刪除指定元素值的元素,時間複雜度爲O(n)。

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++;
    // 如果index不是最後一位,則將index之後的元素往前挪一位
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    // 將最後一個元素刪除,幫助GC
    elementData[--size] = null; // clear to let GC do its work
}

removeAll(Collection<?> c)方法

批量刪除,也可以單方向求差集,複雜度o(n).

 public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
     	//刪除所有在集合c中的元素
        return batchRemove(c, false);
    }

//批量刪除,false代表在集合中的元素統統刪除
private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            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;
    }

retainAll(Collection<?> c)方法

求交集,複雜度o(n)

public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
    //此處爲true,表明刪除所有不在c中的元素
        return batchRemove(c, true);
    }

set(int index, E element) 方法

更新元素,複雜度o(1)

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

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

迭代器方法

ArrayList中的迭代器有兩種。

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

public ListIterator<E> listIterator() {
        return new ListItr(0);
    }


public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }


//迭代器
private class Itr implements Iterator<E> {
      //可以向後遍歷
    }

//list迭代器   
private class ListItr extends Itr implements ListIterator<E> {
       //既可以向前也可以向後遍歷 
    }



3、總結

(1)ArrayList內部使用數組存儲元素,當數組長度不夠時進行擴容,每次加一半的空間,ArrayList不會進行縮容;

(2)ArrayList支持隨機訪問,通過索引訪問元素極快,時間複雜度爲O(1);

(3)ArrayList支持求並集,調用addAll(Collection<? extends E> c)方法即可;

(4)ArrayList支持求交集,調用retainAll(Collection<? extends E> c)方法即可;

(5)ArrayList支持求單向差集,調用removeAll(Collection<? extends E> c)方法即可;

(6)ArrayList有兩種迭代器,支持向前向後遍歷。

(7)ArrayList底層基於數組,所以除了迭代器,也可以使用循環遍歷。

(8)ArrayList查詢快,增刪慢,線程不安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章