ArrayList 是我們常用的集合之一。從名稱可以看出,ArrayList 必然和Array有著不可或缺的聯系。
我們看看ArrayList 比較核心的操作
- 以什么樣的數據結構來存儲數據的?
- 怎么初始化的?做了哪些事情?
- add中做了哪些操作呢?
- add中的擴容怎么擴容的?
- 怎么get數據呢?
我們先看看在ArrayList中的一些操作性的東西
默認參數
private static final int DEFAULT_CAPACITY = 10; // 默認數組容量
private static final Object[] EMPTY_ELEMENTDATA = {}; //空Object數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //空Object數組 初始化使用
transient Object[] elementData; //存儲數據的數組
private int size; // 實際有值的容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 建議的數組容量值
初始化
初始化分為三種情況
- 無參數初始化;
將存儲對象的數組設置為空的Object數組 - 根據容量初始化;
根據傳遞的容量大小初始化容量,并初始化為Object數組 - 根據集合數據初始化;
將集合轉為數組后,設置給存儲數組,然后將數組轉換為Object數組
無參數就好比我們做老式大巴車一樣,生意不好的時候只有一個空車,連座位都沒有這種,等有人上車再給一個個安排座位;司機如果懶就先放了些空位,等后面有人上車不用安排座位了,直接坐就好了,等空位滿了再一個個安排,這就是容量初始化;過年過節人多了,就直接所有人上車,然后安排各自座位;
// 默認無參初始化
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//設置為空數組
}
// 初始化容量的實現
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity]; // 初始化 Object數組
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA; //設置為空Object數組
} else {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
}
// 參數為集合的初始化
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); //將傳遞的集合轉數組
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class) // 非Object 數組
elementData = Arrays.copyOf(elementData, size, Object[].class);//將數組轉為Object數組
} else {
this.elementData = EMPTY_ELEMENTDATA; //設置為空Object數組
}
}
add ?
當有人上車,會出現兩種情況;
(1) 有人上車呢,隨便做,司機就給安排到最后做去了;
(2) 有人非要根據座位號去坐車,怎么辦呢?司機直接把座位后面的人一個個往后排了,然后給他讓出空位置給他坐;
無論這上面那種情況,都會涉及到一個問題,預先安排的空座位不夠了,需要加座位了,司機只有來一個人加一個,來一雙加兩個了。
看看代碼中是怎么實現的呢
/**
* 根據下標添加數據
* @param e 添加的對象
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
/**
* 直接添加數據
* @param e 添加的對象
*/
public boolean add(E e) {
// 驗證車(數組)時候還有位置,沒了則給車加位置(擴容),保證容量足夠,所有人都能坐著
ensureCapacityInternal(size + 1);
elementData[size++] = e; // 將入參對象追加到末尾,并對size + 1
return true;
}
/**
* 處理容量
* @param minCapacity 最小容量
*/
private void ensureCapacityInternal(int minCapacity) {
// 先調用計算容量函數
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 計算需要的座位數(容量)
* @param minCapacity 需要的最小容量
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 判斷數組是否為初始化的Object 數組 ,是則根據 需要的最小容量 和默認容量取大值,不是則返回最小容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 判斷有沒有位置,沒位置則加位置(擴容)
* @param minCapacity 最小容量
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // modCount 計數
// 當最小容量大于數組的容量時,進行擴容操作
if (minCapacity - elementData.length > 0)
grow(minCapacity); //擴容函數
}
擴容?
司機在安排新座位的時候,都會重新給在車上的人再安排一下,來達到加座位的目的;智障啊!沒辦法,程序就是這么智障
/**
* 擴容
* @param minCapacity 需要的最小容量
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);// 對已經存在的數組進行擴容copy
}
/**
* 獲取車最大的載客數
* @param minCapacity 所有的人(需要的容量)
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 當需要的容量大于數組的容量時,返回最大的容量為 Integer.MAX_VALUE 否則 MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
get 呢 ?
車到達目的地后,看看有哪些人該下車了,直接根據座位號就可以找到人,然后通知下車咯!
public E get(int index) {
rangeCheck(index); //驗證長度
return elementData(index);//返回 index 的值
}
/**
* 驗證index的值是否大于當前數據的總容量值
* @param index 下標
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 根據下標獲取數據
* @param index 下標
*/
E elementData(int index) {
return (E) elementData[index]; //獲取index 的值
}
我們來分析下結論
- ArrayList 的底層數據結構為Array;
- ArrayList 和Array 一樣,下標都是從0開始;
- 數組默認的初始化容量大小為 10;
- 當Array的沒有空位置的時候,每次新增,都會進行擴容操作,效率是及其低下的;
- 如果在數據已知的情況下可以初始化數組的大小,可以節約性能,不過也不可以盲目的設置初始化大小值,以免占用空間;
進階全棧.jpg