使用java語言實現一個動態數組(詳解)(數據結構)


 

廢話不多說,上代碼

1.從類名開始(我真是太貼心了,給自己點個贊)

public class Array<E>

首先數組類需要帶有泛型,這個不多說。需要注意的是在java中,數組只能存放同一個類型的。


2.成員變量

private int size; //數組中元素的個數
private E[] data; //數組聲明


插個題外話:
關於size和索引,最開始學數組時讓我很傷神,首先數組的索引是從0開始,而size是指數組中元素的
的個數,假設數組中有3個元素,那么size=3,而索引則為0,1,2。它們是差一位的,這個神奇的設計讓我每次在寫循環的界限條件時,
總要換算一下。
比如,遍歷出數組的所有元素

for (int i = 0; i < size; i++) { }


我的心路歷程是這樣的:
  首先,第一步想,從0開始,到最后一位元素的索引位置結束,那么最后一位元素的索引是應該進來for循環的,那么i就應該
小於最后一位元素的索引的下一位,那么最后一位元素的索引的下一位是誰呢,對哦,size比索引大一位,那么應該是size,
所以應該i<size;
如果每次寫for循環的界限時,都要這么想一下,白白消耗腦力阿。是不是只有我這么笨。
最后我的辦法是轉化成來記在腦海里。每次用到的時候,直接腦海里浮現出這個圖。

學習的本質就是將復雜的東西簡單化。

 


3.構造方法
一種用戶指定初始數組容量
一種用戶不指定初始數組容量

public Array (int capacity) {
data = (E[])new Object[capacity];
size = 0;
}

public Array () {
this(10); //調用另一個構造方法,並默認初始容量為10
}


4.居家必備的基本方法

//獲得數組元素個數
public int getSize () {
return size;
}
//獲得數組長度
public int getCapacity () {
return data.length;
}
//獲得數組是否為空
public boolean isEmpty () {
return size == 0;
}

 

5.添加方法

數組添加的本質就是:從后往前到指定索引位置,每個元素向后移一個格,給新來的騰出個地方。
重點:從后往前
//向數組指定位置添加元素,index為指定索引位置,e為添加的值
public void add (int index, E e) {
//索引位置不能讓它瞎插,索引為負數,或者跳格子插,不可以。
if (index < 0 || index > size) {
throw new IllegalArgumentException("add is fail, require index < 0 || index > size");
}
//當數組容量滿了的時候,調用擴容方法,此處給它擴當前數組長度的兩倍。
if (data.length == size) {
this.resize(data.length * 2);
}for (int i = size - 1; i >= index; i--) {
data[i+1] = data[i];
}
//新來的進坑
data[index] = e;
//維護size
size ++;

}

 

//向數組第一位添加元素
public void addFirst (E e) {
//直接復用上一個add方法
this.add(0, e);
}
//向數組最后一位添加元素
public void addLast (E e) {
//同理
this.add(size, e);
}

 


6.刪除方法(我個人分為兩種,一種根據索引刪除,一種根據值刪除)

刪除的本質:和添加相反,從要刪除的索引位置的下一位開始,到最后一位元素索引位置結束,依次向前占一個坑。
重點:遍歷的時候從前往后
//根據索引刪除某個元素 返回刪除的元素
public E remove (int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("remove is fail,require index < 0 || index >= size");
}
//先把要刪除的元素存起來,不然等會就給覆蓋了。
E value = data[index];
for (int i = index + 1; i < size; i++) {
data[i-1] = data[i];
}
//維護size
size --;
//此處為什么設置為null呢,因為泛型的原因,傳進來的都是類對象,數組中存的是引用地址,引用不斷開的話,垃圾回收器沒辦法回收。
data[size] = null;
//此處縮容,當數組元素個數等於數組長度四分之一時,進行縮容
if (size == data.length/4 && data.length / 2 != 0) { //縮容為數組長度的二分之一 this.resize(data.length /2); } return value; }

問題來了,為什么不在二分之一時就進行縮容呢?而是四分之一呢?
此處涉及到復雜度震盪問題,比較極端的一個情況是: 比如容量為10的一個數組, 此時該數組滿了,此時要進來個元素,然后數組進行擴容,那么添加完元素此時數組的情況為容量為20, 內部有11個元素。 此時我再對數組進行刪除一個元素,刪除之后,數組元素個數變為10個,恰好為數組長度的二分之一, 那么自動進行縮容,以此類推,反復操作,每次擴容縮容的時間復雜度為O(n),所以此處應用了lazy的解決方案 就是等到數組元素個數為數組長度的四分之一時,再進行縮容,就可以避免這個問題。

 

//根據值刪除某個元素
public void removeByValue (E e) {
//復用根據值查找元素的方法,返回索引(此方法在下面)
int index = this.getByElement(e);
if (index != -1) {
//復用根據索引刪除的方法
this.remove(index);
}
}
//刪除第一個元素
public E removeFirst () {
return this.remove(0);
}
//刪除最后一個元素
public E removeLast () {
return this.remove(size - 1);
}

 


7.查找方法(同樣分為兩種,一種根據索引,一種根據值

//根據索引查找數組某個元素,返回值
public E getByIndex (int index) {

if (index < 0 || index >= size) {
throw new IllegalArgumentException("get is fail, require index < 0 || index >= size");
}

return data[index];
}

 

//根據值查找數組某個元素,返回索引
public int getByElement (E e) {
//本質:遍歷數組進行比對
for (int i = 0; i < size; i++) {
if (data[i].equals(e) ) {
return i;
}
}
return -1;
}

 

//是否包含該元素
public boolean contains (E e) {
//本質:遍歷數組進行比對
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) {
return true;
}
}
return false;
}

 


8.修改方法

//修改數組某個元素
public void set (int index, E e) {

if (index < 0 || index >= size) {
throw new IllegalArgumentException("set is fail, require index < 0 || index >= size");
}

data[index] = e;
}

 


9.擴容方法
擴容的本質:就是開辟個新數組,把舊數組的內容復制過去

private void resize (int newCatacity) {
E[] newData = (E[])new Object[newCatacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
//給成員變量data重新賦值新引用(后面有內存圖介紹)
data = newData;
}

畫個擴容引用轉換的內存圖

測試代碼

 public static void main(String[] args) {
        Array<Integer> array = new Array(5);
        array.addLast(1);
        array.addLast(2);
        array.addLast(3);
        array.addLast(4);
        array.addLast(5);
}

 

 

 

 


10.toString方法
本質就是:創建一個StringBuilder對象,然后通過append方法,將數組的內容遍歷,添加進StringBuilder對象。

@Override
public String toString () {
StringBuilder stringBuilder = new StringBuilder();
//Format(String, Object, Object) 將指定的 String 中的格式項替換為兩個指定的 Object 實例的值的文本等效項。
stringBuilder.append(String.format("size =%d ,capacity =%d\n ", size, data.length));
stringBuilder.append('[');
for (int i = 0; i < size; i++) {
stringBuilder.append(data[i]);
if (i != size -1) {
stringBuilder.append(',');
}
}
stringBuilder.append(']');

return stringBuilder.toString();
}

 

最后,

浮於表面看千遍,

不如自己思一遍,

希望這篇文章能夠對你起到幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM