Java原始碼篇之容器類——ArrayList
Java原始碼篇之容器類——ArrayList
1、前言
對於平常開發的時候遇到的ArrayList,在此做一個簡單的原始碼閱讀記錄,JDK1.8版本。
2、ArrayList的類關係
首先看下ArrayList的類關係圖,可以看到實現了
-
Serializable介面,支援序列化與反序列化;
-
Cloneable介面,可以被克隆;
-
RandomAccess介面,支援隨機訪問,另外fori迴圈會比迭代器迴圈效率高,程式碼如下:
for (int i=0, n=list.size(); i < n; i++) list.get(i);
比這個迴圈執行得更快:
for (Iterator i=list.iterator(); i.hasNext(); ) i.next();
3、ArrayList的原始碼
一、類的屬性
/**
* 預設初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* (用於空例項的)共享空陣列例項
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* (用於預設大小的空例項的)共享空陣列例項,我們將其與空的元素資料區分開來,
* 以瞭解何時新增第一個元素.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 儲存ArrayList元素的陣列緩衝區。ArrayList的容量是此陣列緩衝區的長度。任何
* elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA elementData
* 將在新增第一個元素時擴充套件到預設容量(10)。
*/
transient Object[] elementData; // transient關鍵字標記的成員變數不參與序列化過程
/**
* ArrayList的大小(包含的元素數)
*/
private int size;
/**
* 可分配的陣列的最大大小,可能會導致OutOfMemory錯誤:Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
二、add()方法
// 預設往陣列末尾新增元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 大小加1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) { // minCapacity = size + 1
// elementData 是成員變數
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; // 陣列非空時,返回 minCapacity = size + 1
}
private void ensureExplicitCapacity(int minCapacity) { // minCapacity = size + 1
modCount++; // 計數
// 陣列元素個數加1之後如果大於當前陣列長度,則進行擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) { // minCapacity = size + 1
int oldCapacity = elementData.length; // 舊陣列的長度
int newCapacity = oldCapacity + (oldCapacity >> 1); // 新陣列的長度 = 1.5倍舊陣列長度
// 新陣列長度小於陣列元素個數加1
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新陣列長度大於陣列最大值
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 建立一個新的陣列,並把舊陣列元素複製過去,newCapacity為新陣列大小
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
// 新陣列長度大於陣列最大值,並且minCapacity<0才會丟擲oom錯誤
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
// 指定位置新增元素
public void add(int index, E element) {
// 下標合法性校驗
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
// 生成一個index位置為null的新陣列
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
三、get()方法
public E get(int index) {
// 檢查合法性
rangeCheck(index);
// 返回陣列元素
return elementData(index);
}
private void rangeCheck(int index) {
// 下標位置大於等於陣列長度的時候(陣列下標從0開始),丟擲下標越界異常
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
四、remove()方法
// 刪除指定下標的元素
public E remove(int index) {
// 校驗合法性
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
// 刪除指定的元素,第一個出現的
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
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
}
五、set()方法
// 將index位置的元素置換成element
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
4、總結
- 通過閱讀原始碼可以看出,ArrayList的這些方法都沒有加鎖,所以在多執行緒環境下是不安全的;
- add()和remove()方法都需要建立新的陣列,是比較消耗效能的;
相關文章
- Java容器類框架分析(1)ArrayList原始碼分析Java框架原始碼
- Java容器原始碼學習--ArrayList原始碼分析Java原始碼
- Java類集框架 —— ArrayList原始碼分析Java框架原始碼
- Java集合之ArrayList原始碼解析Java原始碼
- 容器類原始碼解析系列(一) ArrayList 原始碼分析——基於最新Android9.0原始碼原始碼Android
- 死磕 java集合之ArrayList原始碼分析Java原始碼
- Java——ArrayList原始碼解析Java原始碼
- 搞懂 Java ArrayList 原始碼Java原始碼
- 詳解Java 容器(第③篇)——容器原始碼分析 - ListJava原始碼
- 詳解Java 容器(第④篇)——容器原始碼分析 - MapJava原始碼
- 詳解Java 容器(第⑤篇)——容器原始碼分析 - 併發容器Java原始碼
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- JAVA集合:ArrayList原始碼分析Java原始碼
- 【Java集合】ArrayList原始碼分析Java原始碼
- Java容器類框架分析(2)LinkedList原始碼分析Java框架原始碼
- Java容器類框架分析(5)HashSet原始碼分析Java框架原始碼
- java基礎:ArrayList — 原始碼分析Java原始碼
- Java 集合框架------ArrayList原始碼分析Java框架原始碼
- java集合原始碼分析(三):ArrayListJava原始碼
- Java集合原始碼學習(2)ArrayListJava原始碼
- Java 8 ArrayList 原始碼簡單分析Java原始碼
- JAVA ArrayList集合底層原始碼分析Java原始碼
- Java集合原始碼分析之開篇Java原始碼
- Java Collection介面 ArrayList集合(容器)Java
- 集合框架原始碼學習之ArrayList框架原始碼
- Java容器類原始碼分析之Iterator與ListIterator迭代器(基於JDK8)Java原始碼JDK
- Java 容器系列(七):HashMap 原始碼分析01之建構函式、內部類JavaHashMap原始碼函式
- 面試必會之ArrayList原始碼分析以及手寫ArrayList面試原始碼
- Java 集合 ArrayList 原始碼分析(帶著問題看原始碼)Java原始碼
- Java集合-ArrayList原始碼解析-JDK1.8Java原始碼JDK
- [原始碼分析]ArrayList原始碼
- ArrayList 原始碼分析原始碼
- ArrayList原始碼解析原始碼
- ArrayList原始碼分析原始碼
- 【Java】Java容器篇(二),深入理解List集合類Java
- Java 集合之ArrayListJava
- Java集合之ArrayListJava
- Java 8 中 ArrayList 的變化原始碼分析Java原始碼