在平時Java
,儲存資料需要用到列表,而大多時候都能用到ArrayList
,比如Mybatis
查詢資料列表,返回列表都是ArrayList
,很多資料的存放也用到了ArrayList
。
jdk 版本: 1.8
ArrayList
是基於大小可變的陣列實現,並允許新增null
值, 根據下標就能資料查詢快。陣列一旦初始化就確定好了大小,如果需要新增資料,就需要做資料的複製,這些操作比較耗時。
ArrayList
陣列的擴容、新增和刪除需要用到陣列的拷貝,主要用到了以下兩個方法:
System.arraycopy
Arrays.copyOf
System.arraycopy()
是Java
的原生的靜態方法,該方法將源陣列元素拷貝到目標陣列中去。
引數詳解:
src
源陣列,起始索引為srcPos
,個數為length
,複製到起始索引為destPos
的dest
目標陣列。比如:
int[] srcArray = new int[]{1,2,3,4,5,6};
int[] desArray = new int[5];
System.arraycopy(srcArray,1,desArray,2,3);
System.out.println("desArray: " + Arrays.toString(desArray));
輸出:
[0, 0, 2, 3, 4]
Arrays.copyOf
本質也是呼叫System.arraycopy
方法:
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
首先建立一個新的陣列copy
,將original
陣列全部複製給copy
陣列。
// 底層陣列
transient Object[] elementData;
// 陣列個數大小
private int size;
// 預設空陣列
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
ArrayList
是基於陣列的一個實現,elementData
就是底層的陣列。size
是記錄陣列的個數。public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
無參構造方法,建立資料直接賦值一個空陣列。
賦一個初始化容量initialCapacity
:
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);
}
}
initialCapacity
大於零,新建長度initialCapacity
的陣列。initialCapacity
等於零,新建一個空陣列。新增資料有兩個方法:
在列表尾部新增資料:
/**
* Appends the specified element to the end of this list.
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal
判斷新增資料後的容量是否足夠,如果不夠,做擴容操作。
private void ensureCapacityInternal(int minCapacity) {
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;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureCapacityInternal
作用是判斷容量當前容量是否足夠存放新的資料,如果不夠就做擴容操作。
calculateCapacity
計算容量,如果陣列為null
,返回minCapacity
和10
的最大值,否則返回minCapacity
。這個方法就是返回陣列的大小。
呼叫無參構造方法
ArrayList()
,再呼叫add()
方法,首先陣列容量會變成10
。這也是為什麼無參構造方法ArrayList()
上面有會有註解Constructs an empty list with an initial capacity of ten.
ensureExplicitCapacity
判斷需要的容量minCapacity
大於當前陣列的長度elementData.length
,將會做擴容處理,也是呼叫grow
方法:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新長度是原來長度的 1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 新長度比需要長度更小,賦值給新長度
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 拷貝陣列到新的長度陣列
elementData = Arrays.copyOf(elementData, newCapacity);
}
group
主要做了兩件事:
做完判斷是否要做擴容之後,直接在size位置上賦值要新增的元素,然後size再自增。
elementData[size++] = e;
10
,其他容量返回當前陣列size + 1
。1.5倍
,將陣列賦值給長度為原來陣列1.5
倍的新陣列。size
賦值。此新增資料是在指定的下標新增資料。
public void add(int index, E element) {
// 下標是否越界
rangeCheckForAdd(index);
// 判斷是否要擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 複製i到size的資料到i+1 到size +1
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
add(int index, E element) 在index
下標新增資料,
index
是否在0 ~size
之間。1.5
倍擴容。index
以後的資料全部往後移動一位。index
下標資料。獲取資料只有通過陣列下標獲取資料 get(int index)
:
public E get(int index) {
//檢查是否超過陣列範圍
rangeCheck(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
// 通過下標獲取資料
return (E) elementData[index];
}
這裡獲取資料就比較簡單:
刪除列表中第一個匹配的元素:
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;
}
判斷要刪除資料是否為null
:
elementData[index]
是否為空。equals
是否匹配elementData[index]
。上面判斷都為true
時,呼叫fastRemove
方法:
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
}
移動陣列資料,index+1
以及之後的資料往前移動一位,最後一位size-1
賦值為null
。
根據index
下標刪除資料。
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;
}
System.arraycopy
方法,System.arraycopy
是將src
源陣列,起始索引為srcPos
,個數為length
,複製到起始索引為destPos
的dest
目標陣列。ArrayList
主要是基於Object[] elementData
動態陣列實現。add
新增陣列首先判斷是否需要擴容,需要擴容,長度擴大成原來的1.5
倍。
size
賦值新增資料index
往後移動一位,新資料新增到index
上。get(int index)
利用陣列的特定,根據下標獲取資料。remove(int index)
將index
之後的資料全部往前移動一位,size - 1
賦為null
。