ArrayList實現原理以及原始碼解析(補充JDK1.7,1.8)
阿新 • • 發佈:2018-11-01
ArrayList實現原理以及原始碼解析(補充JDK1.7,1.8)
ArrayList的基本知識在上一節已經討論過,這節主要看ArrayList在JDK1.6到1.8的一些實現變化。
JDK版本不一樣,ArrayList類的原始碼也不一樣。
1、ArrayList類結構:
1.elementData 是”Object[] 型別的陣列”,它儲存了新增到ArrayList中的元素。實際上,elementData是個動態陣列,我們能通過建構函式 ArrayList(int initialCapacity)來執行它的初始容量為initialCapacity;
如果通過不含引數的建構函式ArrayList()來建立 ArrayList,則elementData的容量預設是10。
elementData陣列的大小會根據ArrayList容量的增長而動態的增長,具 體的增長方式,請參考原始碼分析中的ensureCapacity()函式。
2.size 則是動態陣列的實際大小。
ArrayLis < E>是支援泛型的,所以ArrayList可以構造成任何型別的動態陣列。
同時它也繼承了AbstractList抽象類,抽象類實現了很多預設的方法,但是還有一些方法還是抽象方法。
實現了通用的List列表介面,這裡面定義了List列表的基礎方法。
同時實現了RandomAccess,Cloneable,Serializable介面,這三個介面都是空介面,裡面沒有任何方法宣告。
RandomAccess
是一個標記介面,其主要就是提供給List介面用的,用來表明其支援快速隨機訪問。
因為這個介面是沒有任何實現的,實現了這個介面的類,就表明這個類支援快速訪問,就相當於實現了Serializable就等於支援序列化和反序列化,這是個標準。
Cloneable介面,表明這個是可以進行淺拷貝的,是可以呼叫Object.clone()返回該物件的淺拷貝。
Serializable介面是我們經常遇到的介面,表明該類支援序列化和反序列化操作。
ArrayList繼承這三個沒有任何方法定義的介面只是為了表明這個類是支援隨機快速訪問的,可以支援淺拷貝的,可以被序列化和反序列化的。
2、建構函式(1.6,1.8不同)
1.初始情況:elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; size = 0;
2.當向陣列中新增第一個元素時,通過add(E e)方法中呼叫的ensureCapacityInternal(size + 1)方法,即ensureCapacityInternal(1);
3.在ensureCapacityInternal(int minCapacity)方法中,可得的minCapacity=DEFAULT_CAPACITY=10,然後再呼叫ensureExplicitCapacity(minCapacity)方法,即ensureExplicitCapacity(10);
4.在ensureExplicitCapacity(minCapacity)方法中呼叫grow(minCapacity)方法,即grow(10),此處為真正具體的陣列擴容的演算法,在此方法中,通過elementData = Arrays.copyOf(elementData, 10)具體實現了elementData陣列初始容量為10的構造。
3、調整陣列的容量(擴容)
從add()與addAll()方法中可以看出,每當向陣列中新增元素時,都要去檢查新增元素後的個數是否會超出當前陣列的長度,如果超出,陣列將會進行擴容,以滿足新增資料的需求。
陣列擴容實質上是通過私有的方法ensureCapacityInternal(int minCapacity) -> ensureExplicitCapacity(int minCapacity) -> grow(int minCapacity)來實現的,
但在jdk1.8中,向用戶提供了一個public的方法ensureCapacity(int minCapacity)使使用者可以手動的設定ArrayList例項的容量,以減少遞增式再分配的數量。
此處與jdk1.6中直接通過一個公開的方法ensureCapacity(int minCapacity)來實現陣列容量的調整有區別。
這種演算法構造出來的新的陣列長度的增量都會比上一次大( 而且是越來越大) ,即認為客戶需要增加的資料很多,而避免頻繁newInstance 的情況。位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新為舊容量的1.5倍,效率比原來快。
4、新增元素
ArrayList提供了add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)這些新增元素的方法。
ArrayList提供了remove(int index)、remove(Object o)、clear()、removeRange(int fromIndex, int toIndex)、removeAll(Collection<?> c)、retainAll(Collection<?> c)這些刪除元素的方法。
ArrayList提供了set(int index, E element)方法來修改指定索引上的值。
ArrayList提供了get(int index)、contains(Object o)、indexOf(Object o)、lastIndexOf(Object o)、get(int index)這些查詢元素的方法。
trimToSize()、size()、isEmpty()、clone()、toArray()、toArray(T[] a)
private transient Object[] elementData;
上面這個物件陣列就是其儲存元素的資料結構,前面有一個java關鍵字transient,這個關鍵字是去序列化的意思,即,在這個類序列化後儲存到磁碟或者輸出到輸出流的時候,這個物件陣列是不被儲存或者輸出的。
為什麼要去序列化呢?
這就跟這個ArrayList的特性有關,我們知道ArrayList的容量,也就是這個陣列的容量,一般都是預留一些容量,等到容量不夠時再拓展,那麼就會出現容量還有冗餘的情況,如果這時候進行序列化,整個陣列都會被序列化,連後面沒有意義空元素的也被序列化。
這些是不應該被儲存的。所以java的設計者,就為這個類提供了一個writeObject方法,在實現了Serializable介面的類,如果這個類提供了writeObject方法,那麼在進行序列化的時候就會通過writeObject方法進行序列化,所以ArrayList的writeObject方法就會顯式的為每個實際的陣列元素進行序列化,只序列化有用的元素。
第一:在容量進行擴充套件的時候,其例項如整除運算將容量擴充套件為原來的1.5倍加1,而jdk1.7是利用位運算,從效率上,jdk1.7就要快於jdk1.6。
第二:在算出newCapacity時,其沒有和ArrayList所定義的MAX_ARRAY_SIZE作比較,為什麼沒有進行比較呢,原因是jdk1.6沒有定義這個MAX_ARRAY_SIZE最大容量,也就是說,其沒有最大容量限制的,但是jdk1.7做了一個改進,進行了容量限制。
參考資料:
JDK API ArrayList
ArrayList 原始碼
java原始碼分析之ArrayList
深入Java集合學習系列:ArrayList的實現原理
每天努力一點,每天都在進步。
ArrayList的基本知識在上一節已經討論過,這節主要看ArrayList在JDK1.6到1.8的一些實現變化。
JDK版本不一樣,ArrayList類的原始碼也不一樣。
1、ArrayList類結構:
ArrayList包含了兩個重要的物件:elementData 和 size。//通過ArrayList實現的介面可知,其支援隨機訪問,能被克隆,支援序列化 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { //序列版本號 private static final long serialVersionUID = 8683452581122892189L; //預設初始容量 private static final int DEFAULT_CAPACITY = 10; //被用於空例項的共享空陣列例項 private static final Object[] EMPTY_ELEMENTDATA = {}; //被用於預設大小的空例項的共享陣列例項。其與EMPTY_ELEMENTDATA的區別是:當我們向陣列中新增第一個元素時,知道陣列該擴充多少。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * Object[]型別的陣列,儲存了新增到ArrayList中的元素。ArrayList的容量是該Object[]型別陣列的長度 * 當第一個元素被新增時,任何空ArrayList中的elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA將會被 * 擴充到DEFAULT_CAPACITY(預設容量)。 */ transient Object[] elementData; //非private是為了方便巢狀類的訪問 // ArrayList的大小(指其所含的元素個數) private int size; ...... }
1.elementData 是”Object[] 型別的陣列”,它儲存了新增到ArrayList中的元素。實際上,elementData是個動態陣列,我們能通過建構函式 ArrayList(int initialCapacity)來執行它的初始容量為initialCapacity;
如果通過不含引數的建構函式ArrayList()來建立 ArrayList,則elementData的容量預設是10。
elementData陣列的大小會根據ArrayList容量的增長而動態的增長,具 體的增長方式,請參考原始碼分析中的ensureCapacity()函式。
ArrayLis < E>是支援泛型的,所以ArrayList可以構造成任何型別的動態陣列。
同時它也繼承了AbstractList抽象類,抽象類實現了很多預設的方法,但是還有一些方法還是抽象方法。
實現了通用的List列表介面,這裡面定義了List列表的基礎方法。
同時實現了RandomAccess,Cloneable,Serializable介面,這三個介面都是空介面,裡面沒有任何方法宣告。
RandomAccess
是一個標記介面,其主要就是提供給List介面用的,用來表明其支援快速隨機訪問。
Cloneable介面,表明這個是可以進行淺拷貝的,是可以呼叫Object.clone()返回該物件的淺拷貝。
Serializable介面是我們經常遇到的介面,表明該類支援序列化和反序列化操作。
ArrayList繼承這三個沒有任何方法定義的介面只是為了表明這個類是支援隨機快速訪問的,可以支援淺拷貝的,可以被序列化和反序列化的。
1。1.8在初始化ArrayList結構:
2。1.7在初始化ArrayList結構:
2、建構函式(1.6,1.8不同)
/**
* 構造一個指定初始容量的空列表
* @param initialCapacity ArrayList的初始容量
* @throws IllegalArgumentException 如果給定的初始容量為負值
*/
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);
}
}
// 構造一個預設初始容量為10的空列表
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回的順序排列的
* @param c 包含用於去構造ArrayList的元素的collection
* @throws NullPointerException 如果指定的collection為空
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray()可能不會正確地返回一個 Object[]陣列,那麼使用Arrays.copyOf()方法
if (elementData.getClass() != Object[].class)
//Arrays.copyOf()返回一個 Object[].class型別的,大小為size,元素為elementData[0,...,size-1]
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
// 構造一個預設初始容量為10的空列表:
// 構造一個預設初始容量為10的空列表
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
而在在JDK1.6中,其建構函式為:
- // ArrayList無參建構函式。預設容量是10。
- public ArrayList() {
- this(10);
- }
1.初始情況:elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; size = 0;
2.當向陣列中新增第一個元素時,通過add(E e)方法中呼叫的ensureCapacityInternal(size + 1)方法,即ensureCapacityInternal(1);
3.在ensureCapacityInternal(int minCapacity)方法中,可得的minCapacity=DEFAULT_CAPACITY=10,然後再呼叫ensureExplicitCapacity(minCapacity)方法,即ensureExplicitCapacity(10);
4.在ensureExplicitCapacity(minCapacity)方法中呼叫grow(minCapacity)方法,即grow(10),此處為真正具體的陣列擴容的演算法,在此方法中,通過elementData = Arrays.copyOf(elementData, 10)具體實現了elementData陣列初始容量為10的構造。
3、調整陣列的容量(擴容)
從add()與addAll()方法中可以看出,每當向陣列中新增元素時,都要去檢查新增元素後的個數是否會超出當前陣列的長度,如果超出,陣列將會進行擴容,以滿足新增資料的需求。
陣列擴容實質上是通過私有的方法ensureCapacityInternal(int minCapacity) -> ensureExplicitCapacity(int minCapacity) -> grow(int minCapacity)來實現的,
但在jdk1.8中,向用戶提供了一個public的方法ensureCapacity(int minCapacity)使使用者可以手動的設定ArrayList例項的容量,以減少遞增式再分配的數量。
此處與jdk1.6中直接通過一個公開的方法ensureCapacity(int minCapacity)來實現陣列容量的調整有區別。
/**
* public方法,讓使用者能手動設定ArrayList的容量
* @param minCapacity 期望的最小容量
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
//當elementData為空時,ArrayList的初始容量最小為DEFAULT_CAPACITY(10)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//根據傳入的最小需要容量minCapacity來和陣列的容量長度對比,若minCapactity大於或等於陣列容量,則需要進行擴容,呼叫grow().
//陣列可被分配的最大容量;當需要的陣列尺寸超過VM的限制時,可能導致OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 增加陣列的容量,確保它至少能容納指定的最小容量的元素量
* @param minCapacity 期望的最小容量
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//注意此處擴充capacity的方式是將其向右一位再加上原來的數,實際上是擴充了1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity); //設定陣列可被分配的最大容量
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
//首先得到陣列的舊容量,然後進行oldCapacity + (oldCapacity >> 1),將oldCapacity 右移一位,其效果相當於oldCapacity /2,我們知道位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新為舊容量的1.5倍,然後檢查新容量是否大於最小需要容量,若還是小於最小需要容量,那麼就把最小需要容量當作陣列的新容量,接著,再檢查新容量是否超出了ArrayList所定義的最大容量,若超出了,則呼叫hugeCapacity()來比較minCapacity和MAX_ARRAY_SIZE,如果minCapacity大於最大容量,則新容量則為ArrayList定義的最大容量,否則,新容量大小則為minCapacity。還有一點需要注意的是,容量拓展,是建立一個新的陣列,然後將舊陣列上的陣列copy到新陣列,這是一個很大的消耗,所以在我們使用ArrayList時,最好能預計資料的大小,在第一次建立時就申請夠記憶體。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
jdk1.6中ensureCapacity(int minCapacity)方法:
// 確定ArrarList的容量。
// 若ArrayList的容量不足以容納當前的全部元素,設定 新的容量=“(原始容量x3)/2 + 1”
public void ensureCapacity(int minCapacity) {
modCount++; // 將“修改統計數”+1
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
為什麼在1.7以後ArrayList自動容量擴充選擇擴充1.5倍?
這種演算法構造出來的新的陣列長度的增量都會比上一次大( 而且是越來越大) ,即認為客戶需要增加的資料很多,而避免頻繁newInstance 的情況。位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新為舊容量的1.5倍,效率比原來快。
4、新增元素
ArrayList提供了add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)這些新增元素的方法。
//將指定的元素(E e)新增到此列表的尾部
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//將指定的元素(E e)插入到列表的指定位置(index)
public void add(int index, E element) {
rangeCheckForAdd(index); //判斷引數index是否IndexOutOfBoundsException
ensureCapacityInternal(size + 1); // Increments modCount!! 如果陣列長度不足,將進行擴容
System.arraycopy(elementData, index, elementData, index + 1,
size - index); //將源陣列中從index位置開始後的size-index個元素統一後移一位
elementData[index] = element;
size++;
}
/**
* 按照指定collection的迭代器所返回的元素順序,將該collection中的所有元素新增到此列表的尾部
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
//將陣列a[0,...,numNew-1]複製到陣列elementData[size,...,size+numNew-1]
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
/**
* 從指定的位置開始,將指定collection中的所有元素插入到此列表中,新元素的順序為指定collection的迭代器所返回的元素順序
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index); //判斷引數index是否IndexOutOfBoundsException
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
//先將陣列elementData[index,...,index+numMoved-1]複製到elementData[index+numMoved,...,index+2*numMoved-1]
//即,將源陣列中從index位置開始的後numMoved個元素統一後移numNew位
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//再將陣列a[0,...,numNew-1]複製到陣列elementData[index,...,index+numNew-1]
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
5、刪除元素
ArrayList提供了remove(int index)、remove(Object o)、clear()、removeRange(int fromIndex, int toIndex)、removeAll(Collection<?> c)、retainAll(Collection<?> c)這些刪除元素的方法。
/**
* 移除此列表中指定位置上的元素
* @param index 需被移除的元素的索引
* @return the element 被移除的元素值
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index); //判斷index是否 <= size
modCount++;
E oldValue = elementData(index);
//將陣列elementData中index位置之後的所有元素向前移一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; //將原陣列最後一個位置置為null,由GC清理
return oldValue;
}
//移除ArrayList中首次出現的指定元素(如果存在),ArrayList中允許存放重複的元素
public boolean remove(Object o) {
// 由於ArrayList中允許存放null,因此下面通過兩種情況來分別處理。
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index); //私有的移除方法,跳過index引數的邊界檢查以及不返回任何值
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//私有的刪除指定位置元素的方法,跳過index引數的邊界檢查以及不返回任何值
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
}
//清空ArrayList,將全部的元素設為null
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
//刪除ArrayList中從fromIndex(包含)到toIndex(不包含)之間所有的元素,共移除了toIndex-fromIndex個元素
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;
}
//刪除ArrayList中包含在指定容器c中的所有元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c); //檢查指定的物件c是否為空
return batchRemove(c, false);
}
//移除ArrayList中不包含在指定容器c中的所有元素,與removeAll(Collection<?> c)正好相反
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
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) //判斷指定容器c中是否含有elementData[r]元素
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;
}
6、修改元素
ArrayList提供了set(int index, E element)方法來修改指定索引上的值。
//將指定索引上的值替換為新值,並返回舊值
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
7、查詢元素
ArrayList提供了get(int index)、contains(Object o)、indexOf(Object o)、lastIndexOf(Object o)、get(int index)這些查詢元素的方法。
//判斷ArrayList中是否包含Object(o)
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//正向查詢,返回ArrayList中元素Object o第一次出現的位置,如果元素不存在,則返回-1
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中元素Object 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;
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
//返回指定索引處的值
public E get(int index) {
rangeCheck(index);
return elementData(index); //實質上return (E) elementData[index]
}
8、其他public方法
trimToSize()、size()、isEmpty()、clone()、toArray()、toArray(T[] a)
//將底層陣列的容量調整為當前列表儲存的實際元素的大小的功能
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
//返回ArrayList的大小(元素個數)
public int size() {
return size;
}
//判斷ArrayList是否為空
public boolean isEmpty() {
return size == 0;
}
//返回此 ArrayList例項的淺拷貝
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//返回一個包含ArrayList中所有元素的陣列
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//如果給定的引數陣列長度足夠,則將ArrayList中所有元素按序存放於引數陣列中,並返回
//如果給定的引數陣列長度小於ArrayList的長度,則返回一個新分配的、長度等於ArrayList長度的、包含ArrayList中所有元素的新陣列
@SuppressWarnings("unchecked")
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;
}
9、支援序列化的寫入函式writeObject(java.io.ObjectOutputStream s)和讀取函式readObject(java.io.ObjectInputStream s)
//序列化:將ArrayList的“大小,所有的元素值”都寫入到輸出流中
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
//反序列化:先將ArrayList的“大小”讀出,然後將“所有的元素值”讀出
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();
}
}
}
10、ArrayList是動態陣列,那麼它裡面儲存資料的資料結構是什麼呢?
private transient Object[] elementData;
上面這個物件陣列就是其儲存元素的資料結構,前面有一個java關鍵字transient,這個關鍵字是去序列化的意思,即,在這個類序列化後儲存到磁碟或者輸出到輸出流的時候,這個物件陣列是不被儲存或者輸出的。
為什麼要去序列化呢?
這就跟這個ArrayList的特性有關,我們知道ArrayList的容量,也就是這個陣列的容量,一般都是預留一些容量,等到容量不夠時再拓展,那麼就會出現容量還有冗餘的情況,如果這時候進行序列化,整個陣列都會被序列化,連後面沒有意義空元素的也被序列化。
這些是不應該被儲存的。所以java的設計者,就為這個類提供了一個writeObject方法,在實現了Serializable介面的類,如果這個類提供了writeObject方法,那麼在進行序列化的時候就會通過writeObject方法進行序列化,所以ArrayList的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();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
從程式碼上,我們可以看出區別:
第一:在容量進行擴充套件的時候,其例項如整除運算將容量擴充套件為原來的1.5倍加1,而jdk1.7是利用位運算,從效率上,jdk1.7就要快於jdk1.6。
第二:在算出newCapacity時,其沒有和ArrayList所定義的MAX_ARRAY_SIZE作比較,為什麼沒有進行比較呢,原因是jdk1.6沒有定義這個MAX_ARRAY_SIZE最大容量,也就是說,其沒有最大容量限制的,但是jdk1.7做了一個改進,進行了容量限制。
參考資料:
JDK API ArrayList
ArrayList 原始碼
java原始碼分析之ArrayList
深入Java集合學習系列:ArrayList的實現原理
每天努力一點,每天都在進步。