Java集合ArrayList 源碼分析

一、ArrayList簡介

ArrayList 是一個數組隊列,相當於 動態數組。與Java中的數組相比,它的容量能動態增長。它繼承於AbstractList,實現了List, RandomAccess, Cloneable, java.io.Serializable這些接口。

ArrayList 繼承了AbstractList,實現了List。它是一個數組隊列,提供了相關的添加、刪除、修改、遍歷等功能。
ArrayList 實現了RandmoAccess接口,即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,爲List提供快速訪問功能的(實際上只是一個標誌接口,沒有實現,用於標誌判斷)。在ArrayList中,我們即可以通過元素的序號快速獲取元素對象;這就是快速隨機訪問。稍後,我們會比較List的“快速隨機訪問”和“通過Iterator迭代器訪問”的效率。

ArrayList 實現了Cloneable接口,即覆蓋了函數clone(),能被克隆。

ArrayList 實現java.io.Serializable接口,這意味着ArrayList支持序列化,能通過序列化去傳輸。

和Vector不同,**ArrayList中的操作不是線程安全的!**所以,建議在單線程中才使用ArrayList,而在多線程中可以選擇Vector或者CopyOnWriteArrayList。

二、ArrayList的數據結構

ArrayList的數據結構非常簡單,就是數組Object[],只不過在多了一個size(數組大小)

    /**
     * Default initial capacity.
     * 默認的初始容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;
	 /**
     * Shared empty array instance used for empty instances.
     * 共享空數組對象實例(final修飾)
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
  
    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     * 默認大小的共享空數組實例,我們將此與EMPTY_ELEMENTDATA區分開,
     * 以瞭解何時膨脹多少第一個元素被添加。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * ArrayList保存元素的數組,transient修飾,該成員不需要序列化
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     * @serial
     * ArrayList所包含元素的數量
     */
    private int size;

三、ArrayList構造方法

ArrayList有三個構造方法
public ArrayList()
public ArrayList(int initialCapacity)
public ArrayList(Collection<? extends E> c)

也就是有三種方式獲取到一個ArrayList實例

 /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    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;
        }
    }

四、ArrayList的方法

// Collection中定義的API
boolean             add(E object)
boolean             addAll(Collection<? extends E> collection)
void                clear()
boolean             contains(Object object)
boolean             containsAll(Collection<?> collection)
boolean             equals(Object object)
int                 hashCode()
boolean             isEmpty()
Iterator<E>         iterator()
boolean             remove(Object object)
boolean             removeAll(Collection<?> collection)
boolean             retainAll(Collection<?> collection)
int                 size()
<T> T[]             toArray(T[] array)
Object[]            toArray()
// AbstractCollection中定義的API
void                add(int location, E object)
boolean             addAll(int location, Collection<? extends E> collection)
E                   get(int location)
int                 indexOf(Object object)
int                 lastIndexOf(Object object)
ListIterator<E>     listIterator(int location)
ListIterator<E>     listIterator()
E                   remove(int location)
E                   set(int location, E object)
List<E>             subList(int start, int end)
// ArrayList新增的API
Object               clone()
void                 ensureCapacity(int minimumCapacity)
void                 trimToSize()
void                 removeRange(int fromIndex, int toIndex)
1. 常用方法解析
  • add方法:public boolean add(E e)
    我們這裏重點解析一下ArrayList是如何進行自動擴容的,我們簡單總結一下擴容的基本邏輯
    1. 計算是否需要擴容
    2. 如果需要擴容,則計算擴容,數組結構被修改時會觸發:modCount++,用於跌代時判定數組是否被修改,拋出ConcurrentModificationException 異常,標誌數組已經被修改;
    3. 如果需要擴容,進行擴容過程:數組拷貝到新的數組中,新的數組爲原來數組的 3 / 2 倍
    4. 往新數組中設置添加的元素
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    //確保容量,自動擴容
	 private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
	
	//返回容量大小
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    //
	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;	// 標誌數組的結構被修改次數,每次修改都會加1,主要用於迭代器的判定,如果在迭代其中有修改數組的結構,將拋出 ConcurrentModificationException

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

	/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);		//重點!!擴容的大小是原來的 3/2 倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);		//是否大於最大容量,最大容量是 Integer.MAX_VALUE - 8,如果超過這個容量,則容量爲 Integer.MAX_VALUE
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);		//調用系統的數組複製方法對數組進行復制
    }
  • contains 方法:public boolean contains(Object o)
	//判斷一個列表裏面是否包含某個元素
	public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    //查找某元素的下標,遍歷整個列表
    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;
    }

五、ArrayList支持3種遍歷方式

  • 第一種,通過迭代器遍歷。即通過Iterator去遍歷。
	Integer value = null;
	Iterator iter = list.iterator();
	while (iter.hasNext()) {
    	value = (Integer)iter.next();
	}
  • 第二種,隨機訪問,通過索引值去遍歷。
    由於ArrayList實現了RandomAccess接口,它支持通過索引值去隨機訪問元素。
	Integer value = null;
	int size = list.size();
	for (int i=0; i<size; i++) {
    	value = (Integer)list.get(i);        
	}
  • 第三種,for循環遍歷。如下:
	Integer value = null;
	for (Integer integ:list) {
    	value = integ;
	}

tips: 遍歷ArrayList時,使用隨機訪問(即,通過索引序號訪問)效率最高,而使用迭代器的效率最低!有興趣的小夥伴可以自行測試一下

六、toArray()異常

  • Object[] toArray()
  • T[] toArray(T[] contents)

調用 toArray() 函數會拋出“java.lang.ClassCastException”異常,但是調用 toArray(T[] contents) 能正常返回 T[]。

toArray() 會拋出異常是因爲 toArray() 返回的是 Object[] 數組,將 Object[] 轉換爲其它類型(如如,將Object[]轉換爲的Integer[])則會拋出“java.lang.ClassCastException”異常,因爲Java不支持向下轉型。具體的可以參考前面ArrayList.java的源碼介紹部分的toArray()。
解決該問題的辦法是調用 T[] toArray(T[] contents) , 而不是 Object[] toArray()。

調用 toArray(T[] contents) 返回T[]的可以通過以下幾種方式實現。

// toArray(T[] contents)調用方式一
public static Integer[] vectorToArray1(ArrayList<Integer> v) {
    Integer[] newText = new Integer[v.size()];
    v.toArray(newText);
    return newText;
}

// toArray(T[] contents)調用方式二。最常用!
public static Integer[] vectorToArray2(ArrayList<Integer> v) {
    Integer[] newText = (Integer[])v.toArray(new Integer[0]);
    return newText;
}

// toArray(T[] contents)調用方式三
public static Integer[] vectorToArray3(ArrayList<Integer> v) {
    Integer[] newText = new Integer[v.size()];
    Integer[] newStrings = (Integer[])v.toArray(newText);
    return newStrings;
}
發佈了20 篇原創文章 · 獲贊 3 · 訪問量 5828
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章