ArrayList常用Api分析及注意事項
數組(定長,有序的,隨機訪問)。ArrayList是Java在數組的基礎上進行衍生出來的Java里的一種數據結構,它在擁有數據的特性之外,增加了可變性 (動態數組)。
屬性
屬性 | 備註 |
---|---|
DEFAULT_CAPACITY | 默認初始大小 |
EMPTY_ELEMENTDATA | 空數組,申明了長度可能為0 |
DEFAULTCAPACITY_EMPTY_ELEMENTDATA | 空數組 (無參構造時候的默認值) |
elementData | 承認數組元素 |
size | 數組元素數量,注意和Lenth的 |
三種構造初始化
/**
帶有參數的構造方法,
如果長度為0 則給一個默認的常量。
*/
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);
}
}
/**
直接給一個默認的空數組
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
分析
以上三種構建,兩種有參,一種無參。主要區別就是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
和 EMPTY_ELEMENTDATA
在不同場景分別給值。
EMPTY_ELEMENTDATA
是帶參數的構建函數里長度為0的默認共享數組
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
是不帶參數構造函數里長度為0的共享數組
對比老版本的JDK來看,會對初始化數組的時候解決是數度長度為空的情況下會 new Object[initialCapatity]
的情況,減少不必要的記憶體開支。
在擴容機制也會針對不同的構造出的數組進行不同的擴容機制。
Add方法
public boolean add(E e) {
//確保容量夠不夠,擴容機制
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// step 1
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// step 2. 計算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//判斷是否是無參構造來的,為10個容量大小
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//否則就給即將增長的長度
return minCapacity;
}
// step 3
private void ensureExplicitCapacity(int minCapacity) {
//修改次數
modCount++;
// 即將增長的長度比現的數組長度大就擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// step 4
private void grow(int minCapacity) {
//原始長度
int oldCapacity = elementData.length;
//新長度 = 原始長度 + (原始長度/2)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//盡量不擴容到 Intege 的最大值, 因為有些Vm的設計超過 MAX_ARRAY_SIZE 可能會 OOM錯誤 (OutOfMemoryError: Requested array size exceeds VM limit)
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);
}
分析
- 從
add
函數,可以看出來,每次擴容都是本身的 0.5 倍 - 最大可以擴容到
Intege
最大值,但也是在實際元素數量真的超過MAX_ARRAY_SIZE
的情況下。 - 建立不超過
MAX_ARRAY_SIZE
的原因是OutOfMemoryError: Requested array size exceeds VM limit
- 為了避免開闢過多的數組空間,建立選擇帶參數的構造函數,以量申請。
Remove方法
藉助了系統函數
/**
* 第一個參數是要被複制的數組
*
* 第二個參數是被複制的數字開始複製的下標
*
* 第三個參數是目標數組,也就是要把數據放進來的數組
*
* 第四個參數是從目標數組第幾個下標開始放入數據
*
* 第五個參數表示從被複制的數組中拿幾個數值放到目標數組中
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
把最後一個元素置為null
, size = size -1
elementData[--size] = null; // clear to let GC do its work
注意事項
- 禁止在
for``foreach
里刪除元素。 ArrayList
在多執行緒環境中是不安全的