文章目錄
一、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是如何進行自動擴容的,我們簡單總結一下擴容的基本邏輯- 計算是否需要擴容
- 如果需要擴容,則計算擴容,數組結構被修改時會觸發:modCount++,用於跌代時判定數組是否被修改,拋出ConcurrentModificationException 異常,標誌數組已經被修改;
- 如果需要擴容,進行擴容過程:數組拷貝到新的數組中,新的數組爲原來數組的 3 / 2 倍
- 往新數組中設置添加的元素
/**
* 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;
}