Java集合原始碼學習(2)ArrayList

快樂的博格巴發表於2018-10-02

1 概述

ArrayList是基於陣列實現的,是一個動態陣列,其容量能自動增長,類似於C語言中的動態申請記憶體,動態增長記憶體。

ArrayList不是執行緒安全的,只能用在單執行緒環境下,多執行緒環境下可以考慮用Collections.synchronizedList(List l)函式返回一個執行緒安全的ArrayList類,也可以使用concurrent併發包下的CopyOnWriteArrayList類。

ArrayList實現了Serializable介面,因此它支援序列化,能夠通過序列化傳輸,實現了RandomAccess介面,支援快速隨機訪問,實際上就是通過下標序號進行快速訪問,實現了Cloneable介面,能被克隆。

每個ArrayList例項都有一個容量,該容量是指用來儲存列表元素的陣列的大小。它總是至少等於列表的大小。隨著向ArrayList中不斷新增元素,其容量也自動增長。自動增長會帶來資料向新陣列的重新拷貝,因此,如果可預知資料量的多少,可在構造ArrayList時指定其容量。在新增大量元素前,應用程式也可以使用ensureCapacity操作來增加ArrayList例項的容量,這可以減少遞增式再分配的數量。

注意,此實現不是同步的。如果多個執行緒同時訪問一個ArrayList例項,而其中至少一個執行緒從結構上修改了列表,那麼它必須保持外部同步。

ArrayList繼承AbstractList,實現了List、 RandomAccess、Cloneable、Serializable介面, 為ArrayList內部是用一個陣列儲存元素值,相當於一個大小可變的陣列,也就是動態陣列。 由於ArrayList底層是陣列實現的,所以可以隨機訪問。

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
複製程式碼

Java集合原始碼學習(2)ArrayList
(1)繼承和實現
繼承了AbstractList,實現了List:ArrayList是一個陣列佇列,提供了相關的新增、刪除、修改、遍歷等功能。
實現RandmoAccess介面:即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,為List提供快速訪問功能的在ArrayList中,我們即可以通過元素的序號快速獲取元素物件;這就是快速隨機訪問。
實現了Cloneable介面:即覆蓋了函式clone(),能被克隆。
實現java.io.Serializable介面:這意味著ArrayList支援序列化,能通過序列化去傳輸。

(2)執行緒安全
ArrayList不是執行緒安全的。建議在單執行緒中才使用ArrayList,而在多執行緒中可以選擇Vector或者CopyOnWriteArrayList。同樣,HashMap也是執行緒不安全的,如果需要併發訪問應該使用Hashtable(遺留類)或ConcurrentHashMap。

private static final int DEFAULT_CAPACITY = 10;//預設容量是10
複製程式碼

私有屬性:

/** 
  * The array buffer into which the elements of the ArrayList are stored. 
  * The capacity of the ArrayList is the length of this array buffer. 
  */  
 private transient Object[] elementData;  

 /** 
  * The size of the ArrayList (the number of elements it contains). 
  * 
  * @serial 
  */  
 private int size;
複製程式碼

elementData儲存ArrayList內的元素,size表示它包含的元素的數量。

有個關鍵字需要解釋:transient。

Java的serialization提供了一種持久化物件例項的機制。當持久化物件時,可能有一個特殊的物件資料成員,我們不想用serialization機制來儲存它。為了在一個特定物件的一個域上關閉serialization,可以在這個域前加上關鍵字transient。

Java集合原始碼學習(2)ArrayList
經常在實現了 Serializable介面的類中能看見transient關鍵字。這個關鍵字並不常見。 transient關鍵字的作用是:阻止例項中那些用此關鍵字宣告的變數持久化;當物件被反序列化時(從原始檔讀取位元組序列進行重構),這樣的例項變數值不會被持久化和恢復。當某些變數不想被序列化,同是又不適合使用static關鍵字宣告,那麼此時就需要用transient關鍵字來宣告該變數。

除了以上兩個成員變數,我們還需要掌握一個變數,它是

protected transient int modCount = 0;
複製程式碼

這個變數主要作用是防止在進行一些操作時,改變了ArrayList的大小,那將使得結果不可預測。

建構函式:

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

/**有參構造
 * 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 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;
    }
}
複製程式碼

常用的方法

boolean add(E e) 
將指定的元素新增到此列表的尾部。

void add(int index, E element) 
將指定的元素插入此列表中的指定位置

boolean addAll(Collection<? extends E> c) 
按照指定 collection 的迭代器所返回的元素順序,將該 collection 中的所有元素新增到此列表的尾部。 

boolean addAll(int index, Collection<? extends E> c) 
從指定的位置開始,將指定 collection 中的所有元素插入到此列表中。 

void clear() 
移除此列表中的所有元素。 

Object clone() 
返回此 ArrayList 例項的淺表副本。 

boolean contains(Object o) 
如果此列表中包含指定的元素,則返回 true。 

void ensureCapacity(int minCapacity) 
如有必要,增加此 ArrayList 例項的容量,以確保它至少能夠容納最小容量引數所指定的元素數。 

E get(int index) 
返回此列表中指定位置上的元素。 

int indexOf(Object o) 
返回此列表中首次出現的指定元素的索引,或如果此列表不包含元素,則返回 -1。 

boolean isEmpty() 
如果此列表中沒有元素,則返回 true 

int lastIndexOf(Object o) 
返回此列表中最後一次出現的指定元素的索引,或如果此列表不包含索引,則返回 -1。 

E remove(int index) 
移除此列表中指定位置上的元素。 

boolean remove(Object o) 
移除此列表中首次出現的指定元素(如果存在)。 

protected void removeRange(int fromIndex, int toIndex) 
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之間的所有元素。 

E set(int index, E element) 
用指定的元素替代此列表中指定位置上的元素。 

int size() 
返回此列表中的元素數。 

Object[] toArray() 
按適當順序(從第一個到最後一個元素)返回包含此列表中所有元素的陣列。

void trimToSize() 
將此 ArrayList 例項的容量調整為列表的當前大小。應用程式可以使用此操作來最小化 ArrayList 例項的儲存量。
複製程式碼

遍歷方式:
ArrayList支援3種遍歷方式
1.通過迭代器遍歷。即通過Iterator去遍歷
2.隨機訪問,通過索引值去遍歷,ArrayList實現了RandomAccess介面,它支援通過索引值去隨機訪問元素
3.for迴圈遍歷

遍歷ArrayList時,使用隨機訪問(即通過索引序號訪問)效率最高,而使用迭代器的效率相對較低。

相關文章