四、動态數組的實作
1、添加元素
- 我們知道, 每當數組添加新元素時, 都會在數組最後一個元素的後面添加新元素
- 這個
等于新元素需要添加到的索引
, 在目前數組元素的個數
中ArrayList
屬性就是size
, 是以就是将新元素添加到數組的目前數組元素的個數
位置上, 然後size
加size
1
public void add(E element) {
elements[size] = element;
size++;
}
複制代碼
2、數組擴容
- 由于數組
最大的容量隻有elements
, 是以當數組存滿元素時, 就需要對數組進行擴容10
- 因為數組是無法動态擴容的, 是以需要建立一個新的數組,這個數組的容量要比之前數組的容量大
- 然後在将原數組中的元素存放到新數組中, 這樣就實作了數組的擴容
private void ensureCapacity() {
// 擷取數組目前容量
int oldCapacity = elements.length;
// 如果 目前存儲的元素個數 < 目前數組容量, 直接傳回
if (size < oldCapacity) return;
// 新數組的容量為原數組容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 建立新數組
E[] newElements = (E[]) new Object[newCapacity];
// 原數組中的元素存儲到新數組中
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
// 引用新數組
elements = newElements;
}
複制代碼
- 此時,
方法的實作如下, 在添加新元素之前, 先判斷是否需要擴容add
public void add(E element) {
// 添加新元素之前, 先判斷是否需要擴容
ensureCapacity();
elements[size] = element;
size++;
}
複制代碼
3、删除元素
- 删除元素, 實際上就是去掉
, 并将後面的元素指定位置的元素
向前移動
- 如下圖, 當删除索引為
的元素時, 隻需要将後面的元素向前移動, 然後在去掉最後一個元素,3
減size
即可1
public E remove(int index) {
// 取出需要删除的元素
E element = elements[index];
// 通過循環, 将index後面的所有元素向前移動一位
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
// 删除最後一個元素
elements[--size] = null;
// 将删除的元素傳回
return element;
}
複制代碼
- 注意: 删除元素時傳入的索引不能越界, 即
不能小于0, 也不能大于等于size
- 是以我們在删除元素之前需要先進行索引檢查
private void rangeCheck(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
}
複制代碼
-
方法實作如下remove
public E remove(int index) {
// 判斷索引是否越界
rangeCheck(index);
// 取出需要删除的元素
E element = elements[index];
// 通過循環, 将index後面的所有元素向前移動一位
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
// 删除最後一個元素
elements[--size] = null;
// 将删除的元素傳回
return element;
}
複制代碼
4、數組縮容
- 當數組中的元素删除後, 數組剩餘的空間可能會很大, 這樣就會造成記憶體的浪費
- 是以當數組中元素删除後, 我們需要對數組進行縮容
- 實作方法類似于擴容, 當數組中容量小于某個值時, 建立新的數組, 然後将原有數組中的元素存入新數組即可
public void trim() {
// 擷取目前數組的容量
int capacity = elements.length;
// 當size大于等于容量的一半, 或則容量已經小于預設容量(10)時, 直接傳回
if (size >= capacity >> 1 || capacity < CAPACITY_DEFAULT) return;
// 計算新的容量 = 原有容量的一半
int newCapacity = capacity >> 1;
// 建立新數組
E[] newElements = (E[]) new Object[newCapacity];
// 将原數組元素存入新數組
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
// 引用新數組
elements = newElements;
}
複制代碼
- 每次删除元素後, 判斷是否需要縮容
public E remove(int index) {
// 判斷索引是否越界
rangeCheck(index);
// 取出需要删除的元素
E element = elements[index];
// 通過循環, 将index後面的所有元素向前移動一位
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
// 删除最後一個元素
elements[--size] = null;
// 判斷數組是否需要縮容
trim();
// 将删除的元素傳回
return element;
}
複制代碼
5、清空元素
- 清空元素, 隻需要将所有的元素置為
, 然後null
置為size
public void clear() {
// 清空存儲的資料
for (int i = 0; i < size; i++) {
elements[i] = null;
}
// 将size置為0
size = 0;
}
複制代碼
6、修改元素
- 修改元素時, 隻需要将原有位置的元素替換掉即可, 隻是需要注意一下索引是否越界
public E set(int index, E element) {
// 判斷索引是否越界
rangeCheck(index);
// 取出被替換元素
E oldElement = elements[index];
// 替換元素
elements[index] = element;
// 傳回被替換元素
return oldElement;
}
複制代碼
7、 查詢元素
- 查詢元素, 隻需要将指定索引的元素傳回, 注意索引是否越界即可
public E get(int index) {
rangeCheck(index);
return elements[index];
}
複制代碼
8、插入元素
- 插入元素類似于删除元素, 隻需要将插入位置後面的元素向後移動即可
- 注意: 需要從後向前移動元素, 如果從前向後移動元素, 那麼會進行元素覆寫, 最後出錯
public void add(int index, E element) {
// 從後向前的順序, 将元素向後移動
for (int i = size - 1; i >= index; i--) {
elements[i + 1] = elements[i];
}
// 插入元素
elements[index] = element;
// size+1
size++;
}
複制代碼
- 需要注意的是, 插入元素的索引也不能越界, 不過不同于删除和設定元素時, 插入的位置可以是所有元素的最後, 即size的位置
public void rangeCheckForAdd(int index) {
// 當索引小于0 或 大于 size時 抛出異常
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
}
複制代碼
-
方法如下add
public void add(int index, E element) {
// 檢查索引是否越界
rangeCheckForAdd(index);
// 從後向前的順序, 将元素向後移動
for (int i = size - 1; i >= index; i--) {
elements[i + 1] = elements[i];
}
// 插入元素
elements[index] = element;
// size+1
size++;
}
複制代碼
9、檢視元素位置
- 可以通過循環, 查找元素在數組中的位置
- 注意: 數組中可以存儲
, 而null
是不能調用null
方法的, 是以需要對傳入的元素進行判斷, 如果查找的元素是equals
, 需要單獨處理null
- 當元素存在時傳回索引, 否則傳回變量
的值ELEMENT_ON_FOUND
private static final int ELEMENT_ON_FOUND = -1;
public int indexOf(E element) {
if (element == null) {
for (int i = 0; i < size; i++) {
if (elements[i] == null) return i;
}
}else {
for (int i = 0; i < size; i++) {
if (element.equals(elements[i])) return i;
}
}
return ELEMENT_ON_FOUND;
}
複制代碼
10、是否包含某個元素
- 當元素存在時, 隻需要判斷索引是否等于
ELEMENT_ON_FOUND
public boolean contains(E element) {
// 檢視元素的索引是否為ELEMENT_ON_FOUND即可
return indexOf(element) != ELEMENT_ON_FOUND;
}
複制代碼
11、元素的數量
- 數組元素的數量, 就是size的值
public int size() {
return size;
}
複制代碼
12、數組是否為空
- 判斷
的值是否為size
public boolean isEmpty() {
return size == 0;
}
複制代碼
13、動态數組列印
- 可以重寫
方法, 來列印toString
中的元素ArrayList
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size = ").append(size).append(", [");
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(",");
}
string.append(elements[i]);
}
string.append("]");
return string.toString();
}