java源碼學習(四)ArrayList
ArrayList
? ArrayList是基於數組實現的,是一個動態數組,其容量能自動增長,類似於C語言中的動態申請內存,動態增長內存。
? ArrayList不是線程安全的,只能用在單線程環境下,多線程環境下可以考慮用Collections.synchronizedList(List l)函數返回一個線程安全的ArrayList類,也可以使用concurrent並發包下的CopyOnWriteArrayList類。
? 以下分析的是JDK1.8的ArrayList源碼,跟JDK1.7的區別還是蠻大的。
一、定義
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- 從ArrayList\可以看出它是支持泛型的,它繼承自AbstractList,實現了List、RandomAccess、Cloneable、Java.io.Serializable接口
- AbstractList提供了List接口的默認實現(個別方法為抽象方法)
- List接口定義了列表必須實現的方法
- RandomAccess是一個標記接口,接口內沒有定義任何內容,支持快速隨機訪問,實際上就是通過下標序號進行快速訪問
- 實現了Cloneable接口的類,可以調用Object.clone方法返回該對象的淺拷貝
- 通過實現 java.io.Serializable 接口以啟用其序列化功能。未實現此接口的類將無法使其任何狀態序列化或反序列化。序列化接口沒有方法或字段,僅用於標識可序列化的語義
二、屬性
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ 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. */ 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. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
- ArrayList提供了2個私有屬性,elementData和size;很容易理解,elementData存儲ArrayList內的元素,size表示它包含的元素的數量(實際數量而非容量)
- transient關鍵字的作用:在采用Java默認的序列化機制的時候,被該關鍵字修飾的屬性不會被序列化。
三、構造方法
ArrayList提供了三個構造方法:
// 帶容量參數的構造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 創建一個空數組
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 參數小於0,拋出異常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
// 默認無參構造方法,創建一個容量為10的數組
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 集合作為參數的構造方法,集合轉化為Object[]
// c為null throws NullPointerException
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray 不一定返回的是Object[]
if (elementData.getClass() != Object[].class)
// 數組復制到 elementData
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// c的長度為0,創建容量為0的數組
this.elementData = EMPTY_ELEMENTDATA;
}
}
? 第一個構造方法使用initialCapacity來初始化elementData數組的大小;
? 第二個構造方法創建一個空的容量為10的elementData數組;
? 第三個構造方法將提供的集合轉成數組並給elementData(返回若不是Object[],則轉換為Object[]);
四、方法詳細介紹
1. 元素存儲
關於ArrayList元素的存儲,提供了5個方法:
public E set(int index, E element)
public boolean add(E e)
public void add(int index, E element)
public boolean addAll(Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c)
set方法,替換元素
// 用指定的元素替代此列表中指定位置上的元素,並返回以前位於該位置上的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
// 檢驗index是否在範圍內,不校驗index為負數的情況,index為負數由數組校驗
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 返回指定位置上的元素
E elementData(int index) {
return (E) elementData[index];
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
add(E e)方法,尾部添加元素
// 添加元素到列表的尾部,並返回true
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!(這註釋還兩感嘆號)
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 若是默認容量為10,則minCapacity取minCapacity和DEFAULT_CAPACITY最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 記錄容量擴容次數(Fast Fail機制,多線程)
protected transient int modCount = 0;
private void ensureExplicitCapacity(int minCapacity) {
// 擴容次數+1
modCount++;
// 若需要的容量大於目前elementData的容量,則進行擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// ArrayList容量的最大值(為啥是這個值設定?)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 擴容到指定容量(有溢出處理)
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// oldCapacity >> 1相當於 oldCapacity / 2,則下面的就是擴容50%
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 若擴容50%還沒到minCapacity,則擴容minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 擴容容量 大於最大容量(溢出處理)
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 擴容容量elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // 溢出處理
throw new OutOfMemoryError();
// 擴容的容量是否大於最大數組容量,是則返回整型最大值,否則返回最大數組容量
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
add(int index, E element)方法,指定位置添加元素
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// index後面的元素往後下標+1並復制到elementData
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 參數element賦值給指定位置的elementData
elementData[index] = element;
// 數組實際長度+1
size++;
}
// 校驗索引下標範圍(不能大於數組實際大小以及小於0,是就拋出異常)
// 方法用於 add 和 addAll
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
? public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
-
此方法是System類提供的native方法,用於copy數組所用
-
src:源數組; srcPos:源數組要復制的起始位置; dest:目的數組; destPos:目的數組放置的起始位置; length:復制的長度
-
此方法的功能就是把dest從destPos到destPos+length的元素用src數組的srcPos位置開始替換
-
arraycopy調用了系統的C/C++代碼,在JDK中是看不到的,但在openJDK中可以看到其源碼。該函數實際上最終調用了C語言的memmove()函數,因此它可以保證同一個數組內元素的正確復制和移動,比一般的復制方法的實現效率要高很多,很適合用來批量處理數組。Java強烈推薦在復制大量數組元素時用該方法,以取得更高的效率。
?
```JAVA
public class ArrayCopyTest {
public static void main(String[] args) {
char[] c1 = new String("123456").toCharArray();
char[] c2 = new String("abcdef").toCharArray();
System.arraycopy(c1,2 , c2, 1, 2);
for(char c : c1){
System.out.print(c);
}
System.out.println();
for(char c : c2){
System.out.print(c);
}
}
}
```
結果為:
123456
a34def
addAll(Collection<? extends E> c)方法,尾部按順序添加所有集合元素
// c為null 會拋出NullPointerException
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
// 擴容
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);// 數組copy
size += numNew;// elementData實際大小size增加
return numNew != 0;// 返回是否有新元素添加
}
addAll(int index, Collection<? extends E> c)方法,指定位置按順序添加所有集合元素
// c為null 會拋出NullPointerException
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);// 校驗索引下標範圍
Object[] a = c.toArray();
int numNew = a.length;
// 擴容
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
// elementData數組index後面的元素移動到elementData的末端
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 添加的集合c中的所有元素index到index+numNewcopy進來
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;// elementData實際大小size增加
return numNew != 0;// 返回是否有新元素添加
}
2. 元素讀取
// 返回數組index所在的元素
public E get(int index) {
rangeCheck(index);// 校驗index是否越界
return elementData(index);
}
// 檢驗index是否在範圍內,不校驗index為負數的情況,index為負數由數組校驗
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 返回指定位置上的元素
E elementData(int index) {
return (E) elementData[index];
}
3. 元素刪除
關於ArrayList元素的刪除,提供了4個方法:
public E remove(int index)
public boolean remove(Object o)
public void clear()
public boolean removeAll(Collection<?> c)
public boolean retainAll(Collection<?> c)
remove(int index)方法,刪除指定位置的元素
public E remove(int index) {
rangeCheck(index);// 校驗index向上是否越界,越界則拋出異常
modCount++;// 擴容的時候++,刪除也++,記錄操作次數
E oldValue = elementData(index);// 獲取指定位置的元素
int numMoved = size - index - 1;
// elementData index後面的元素向前移動一位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// --size,elementData尾部元素置為null,GC可以回收
elementData[--size] = null;
return oldValue;// 返回已刪除的元素
}
remove(Object o)方法,刪除數組中第一個為o的元素
public boolean remove(Object o) {
// 遍歷數組,刪除第一個為null的元素並返回
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
// 遍歷數組,刪除第一個為o的元素(用equals判斷)並返回
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
// 遍歷結束還沒有找到o,則返回false,elementData中元素沒做任何變動
return false;
}
// 私有方法,快速刪除index所在的元素,跟上面的remove方法大致邏輯一下,不返回已刪除元素
private void fastRemove(int index) {
modCount++;
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
}
clear()方法,刪除列表中所有的元素
public void clear() {
modCount++;// 記錄操作次數
// 遍歷列表,每個元素都置為null,GC進行回收
for (int i = 0; i < size; i++)
elementData[i] = null;
// 列表size置為0
size = 0;
}
removeAll方法,刪除傳入參數集合中的所有元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);// 判斷c是否為null,null拋出NullPointerException
// 刪除集合中所有元素,這些元素也在c中,取elementData和c的差集即 elementData - c
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
// 新建一個不可變引用,指向List的 elementData數組
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
// 根據complement參數判斷是否保留elementData[r]元素
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// c.contains() 拋出異常處理
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// 下標size-w後面的元素置為空,gc回收;w=size則表示沒有元素變動
if (w != size) {
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;// 相當於執行了多次remove操作
size = w;
modified = true;
}
}
return modified;// 返回elementData是否有元素變更
}
retainAll方法,刪除非傳入參數集合中的所有元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);// 判斷c是否為null,null拋出NullPointerException
return batchRemove(c, true);
}
4. 序列化與反序列化
? ArrayList的序列化重寫了writeObject和readObject方法;
? transient不被序列化
writeObject
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();// 調用默認的序列化方法
// 寫入數組實際長度
s.writeInt(size);
// 真正的序列化操作
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
readObject
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
5. 獲取元素索引值
indexOf
// 返回列表中第一個與o相同的元素所在的索引;如果列表不包含o,則返回-1
public int indexOf(Object o) {
// o為null,返回列表中第一個為null元素的下標
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;
}
lastIndexOf
// 返回列表中最後一個與o相同的元素所在的索引;如果列表不包含o,則返回-1
public int lastIndexOf(Object o) {
// 倒序遍歷
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
contains
// 返回列表是否包含o元素,index = -1則返回false
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
6. ArrayList轉化為數組
Object[] toArray()
public Object[] toArray() {
// 調用Arrays數組copy
return Arrays.copyOf(elementData, size);
}
\ 泛型轉化
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a‘s runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
7. 其他方法
// elementData擴容後會有多余的容量未使用;把elementData的容量減少到size(實際容量)
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
// 返回列表實際大小size
public int size() {
return size;
}
// 返回列表是否為空
public boolean isEmpty() {
return size == 0;
}
// ArrayList 淺copy
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
// elementData copy
v.elementData = Arrays.copyOf(elementData, size);
// modCount操作次數
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn‘t happen, since we are Cloneable
throw new InternalError(e);
}
}
// protect方法 列表數組移除元素(fromIndex到toIndex)
// list.sublist(fromIndex, toIndex).clear()這個方法底層調用
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
// 獲取元素列表叠代器
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
// 返回一個列表叠代器,FastFail機制
public ListIterator<E> listIterator() {
return new ListItr(0);
}
// 返回一個叠代器,FastFail機制
public Iterator<E> iterator() {
return new Itr();
}
// 截取列表
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
* and <em>fail-fast</em> [email protected] Spliterator} over the elements in this
* list.
*
* <p>The [email protected] Spliterator} reports [email protected] Spliterator#SIZED},
* [email protected] Spliterator#SUBSIZED}, and [email protected] Spliterator#ORDERED}.
* Overriding implementations should document the reporting of additional
* characteristic values.
*
* @return a [email protected] Spliterator} over the elements in this list
* @since 1.8
*/
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
// 列表排序
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
五、參考資料
http://blog.csdn.net/jzhf2012/article/details/8540410
http://www.cnblogs.com/xujian2014/p/4625346.html
http://blog.csdn.net/u014082714/article/details/52253331
http://www.cnblogs.com/java-zhao/p/5102342.html
java源碼學習(四)ArrayList