陣列基礎回顧
1、陣列是一種常見的資料結構,用來儲存同一型別值的集合
2、陣列就是儲存資料長度固定的容器,保證多個資料的資料型別要一致
3、陣列是一種順序儲存的線性表,所有元素的記憶體地址是連續的
4、例如:new 一個int基本型別的陣列array
-
int[] array = new int[]{11,22,33};
5、陣列的優勢與劣勢
- 陣列具有很高的隨機訪問能力,通過陣列下標就可以讀取對應的值
- 陣列在插入與刪除元素時,會導致大量的元素移動
- 陣列的長度是固定的,無法動態擴容,在實際開發中,我們更希望陣列的容量是可以動態改變的
- 總結——陣列適用於讀操作多,寫操作少的場景
自定義動態陣列
動態陣列的設計
/**
* 元素的數量
*/
protected int size;
/**
* 陣列所有元素及記憶體地址指向
*/
private E[] elements;
圖示結構:
抽象父類介面設計
將動態陣列與連結串列共同的屬性與方法抽取出,作為抽象類,提高複用性
抽象父類介面——List
public interface List<E> {
//查無元素的返回標誌
int ELEMENT_NOT_FOUND = -1;
/**
* 元素的數量
* @return
*/
int size();
/**
* 是否為空
* @return
*/
boolean isEmpty();
/**
* 設定index位置的元素
* @param index
* @param element
* @return 原來的元素ֵ
*/
E set(int index, E element);
/**
* 獲取index位置的元素
* @param index
* @return
*/
E get(int index);
/**
* 是否包含某個元素
* @param element
* @return
*/
boolean contains(E element);
/**
* 檢視元素的索引
* @param element
* @return
*/
int indexOf(E element);
/**
* 新增元素到尾部
* @param element
*/
void add(E element);
/**
* 在index位置插入一個元素
* @param index
* @param element
*/
void add(int index, E element);
/**
* 刪除index位置的元素
* @param index
* @return
*/
E remove(int index);
/**
* 刪除指定元素
* @param element
* @return
*/
public E remove(E element);
/**
* 清除所有元素
*/
void clear();
抽象父類設計
抽象父類AbstractList
是對介面List
的實現
public abstract class AbstractList<E> implements List<E> {
/**
* 元素的數量
*/
protected int size;
/**
* 元素的數量
* @return
*/
public int size() {
return size;
}
/**
* 是否為空
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 是否包含某個元素
* @param element
* @return
*/
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND;
}
/**
* 新增元素到尾部
* @param element
*/
public void add(E element) {
add(size, element);
}
/**
* 非法索引訪問陣列異常
* @param index
*/
protected void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
/**
* 索引檢查函式
* @param index
*/
protected void rangeCheck(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
/**
* 陣列新增元素的索引檢查函式
* @param index
*/
protected void rangeCheckForAdd(int index) {
//index > size,元素可以新增在陣列size位置,即陣列尾部下一儲存單元
if (index < 0 || index > size) {
outOfBounds(index);
}
}
}
動態陣列之DynamicArray
public class DynamicArray<E> extends AbstractList<E> {
/**
* 陣列所有元素及記憶體地址指向
*/
private E[] elements;
//陣列的預設初始化值
private static final int DEFAULT_CAPACITY = 10;
/**
* 帶參建構函式,引數是陣列初始化值
* @param capacity
*/
public DynamicArray(int capacity) {
//如果傳入的capacity>預設初始值,取capacity,否則取預設值
capacity = Math.max(capacity, DEFAULT_CAPACITY);
//通過new Object[],動態陣列可以實現多物件化
elements = (E[]) new Object[capacity];
}
/**
* 建構函式,將陣列初始化
*/
public DynamicArray() {
this(DEFAULT_CAPACITY);
}
/**
* 設定index位置的元素值
* @param index
* @param element
* @return old
*/
public E set(int index,E element){
//檢查索引是否合法
rangeCheck(index);
//
E old = elements[index];
elements[index] = element;
return old;
}
/**
* 獲取陣列index位置的元素
* @param index
* @return elements[index];
*/
public E get(int index){
rangeCheck(index);
return elements[index];
}
/**
* 檢視元素的索引
* @param element
* @return
*/
public int indexOf(E element){
//如果元素為空,單獨判斷,防止NPE
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_NOT_FOUND;
}
/**
* 新增元素到陣列指定位置
* @param index
* @param element
*/
public void add(int index,E element){
//檢查索引是否合法
rangeCheckForAdd(index);
//檢查陣列容量是否足夠
ensureCapacity(size + 1);
for (int i = size;i > index;i--){
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
/**
* 刪除指定元素
* @param element
* @return
*/
public E remove(E element){
//呼叫indexOf獲取索引,通過索引刪除指定元素
return remove(indexOf(element));
}
/**
* 刪除指定index位置的元素
* @param index
* @return
*/
public E remove(int index){
//檢查索引是否合法
rangeCheck(index);
E old = elements[index];
for (int i = index + 1;i < size;i++){
elements[i - 1] = elements[i];
}
//將陣列原來尾部最後的元素所在的位置置為null,釋放原來地址引用對應的物件記憶體
elements[--size] = null;
//檢測是否需要縮容
trim();
return old;
}
/**
* 清除所有元素
*/
public void clear() {
for (int i = 0; i < size; i++) {
elements[i] = null;
}
size = 0;
}
/**
* 保證要有capacity的容量
* @param capacity
*/
private void ensureCapacity(int capacity){
int oldCapacity = elements.length;
//如果陣列容量足夠,return
if (oldCapacity >= capacity) 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;
System.out.println(oldCapacity + "擴容為" + newCapacity);
}
/**
* 重寫toString函式,列印陣列
* @return
*/
@Override
public String toString() {
// size=3, [99, 88, 77]
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();
}
}
補充陣列縮容
在每次刪除陣列元素時及清空陣列時,進行縮容檢查
/**
* 如果記憶體使用比較緊張,動態陣列有比較多的剩餘空間,可以考慮進行縮容操作
* 例如:經過擴容後的陣列很大,在經過刪除操作後,陣列元素數量不多,而陣列所佔空間夠大
* 比如剩餘空間佔總容量的一半時,就進行縮容 (規則可以自定義)
*/
private void trim(){
int oldCapacity = elements.length;
int newCapacity = oldCapacity >> 1;
//如果元素的數量大於縮容後陣列長度,或者小於初始量,不進行縮容操作
if (size >= (newCapacity) || size <= DEFAULT_CAPACITY) return;;
// 剩餘空間還很多
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
System.out.println(oldCapacity + "縮容為" + newCapacity);
}
/**
* 清除所有元素
*/
public void clear() {
// 陣列清空,應該根據情況縮容,避免記憶體浪費
if (elements != null && elements.length > DEFAULT_CAPACITY) {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}else {
for (int i = 0; i < size; i++) {
elements[i] = null;
}
}
size = 0;
}
全域性的關係圖
宣告
個人能力有限,有不正確的地方,還請指正
文章為原創,歡迎轉載,註明出處即可
本文的程式碼已上傳github
,歡迎star