數組對於每一門編程語言來講都是重要的數據結構之一,固然不一樣語言對數組的實現及處理也不盡相同。Java 語言中提供的數組是用來存儲固定大小的同類型元素。你能夠聲明一個數組變量,如 numbers[100] 來代替直接聲明 100 個獨立變量 number0,number1,....,number99。數組一旦聲明完畢,長度就固定了。java
數組的聲明、建立和初始化。git
package cc.myall.demo01; import org.junit.Test; public class demo01 { @Test public void test01(){ int[] array = new int[10]; int len = array.length; for(int i=0; i < len; i++ ){ System.out.println(array[i]); //默認值是0 } } }
運行的結果就是打印出10個0。github
數組最大的優勢就是查詢快,使用索引來進行查詢;編程
數組的索引最好是有語義的。數組
基於java數組來二次封裝本身的數組類(Array),使之成爲動態數組,也就是java內置的ArrayList類。下面來本身實現一下。數據結構
size指向的是第一個沒有元素的位置。編程語言
新建一個類Array,函數
package cc.myall.demo01; public class Array { private int[] data; //先假定這個數組只能裝int類型 private int size; //構造函數,傳入數組的容量來構造數組 public Array(int capacity) { data = new int[capacity]; size = 0; //實際大小初始爲0 } public Array() { this(10); } //獲取數組中元素的個數 public int getSize() { return size; } // 獲取數組的容量 public int getCapacity() { return data.length; } //判斷數組是否爲空 public boolean isEmpty() { return size == 0; } }
測試:測試
package cc.myall.demo01; import org.junit.Test; public class demo01 { @Test public void test02(){ Array arr = new Array(); System.out.println(arr.getCapacity()); } }
運行結果:10this
修改成支持泛型
public class Array<E> { private E[] data; //先假定這個數組只能裝int類型 private int size; //構造函數,傳入數組的容量來構造數組 public Array(int capacity) { data = (E[]) new Object[capacity]; size = 0; //實際大小初始爲0 } public Array() { this(10); } //獲取數組中元素的個數 public int getSize() { return size; } // 獲取數組的容量 public int getCapacity() { return data.length; } //判斷數組是否爲空 public boolean isEmpty() { return size == 0; } }
addLast(int e) 時間複雜度O(1)
在最後一個元素的後面添加一個元素,原理就是在size處添加,而後讓size加1。在Array類中添加addLast函數。
//在最後一個元素的後面添加元素 public void addLast(E e) { if(size == data.length) { throw new IllegalArgumentException("添加失敗,數組已滿"); } data[size] = e; size ++; }
add(int index, int e) 時間複雜度O(n)
在元素中間的某個位置插入元素,讓改位置及之後的元素一次移動一個位置,即size-1位置的一道size處,以此類推,移動後以下圖,就騰出一個位置來:
這裏說的騰出一個位置並非說改位置爲空,而是能夠插入覆蓋。代碼以下
//向指定index處添加元素 public void add(int index, E e) { if(size == data.length) { throw new IllegalArgumentException("添加失敗,數組已滿"); } if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } for(int i = size-1; i >= index; i --) { data[i+1] = data[i]; } data[index] = e; size ++; }
再看addLast(int e) 時間複雜度O(1)
能夠利用add函數改寫,此時的index就是size。
//在最後一個元素的後面添加元素 public void addLast(E e) { add(size, e); }
addFirst(int e) 時間複雜度O(n)
能夠利用add函數改寫,此時的index就是0。在數組的第一個元素出插入元素
// 在數組的第一個位置添加元素 public void addFirst(E e) { add(0, e); }
get 和 set時間複雜度O(1)
// 獲取index索引處的元素 public E get(int index) { if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } return data[index]; }
// 改變index索引處的元素 public void set(int index, E e) { if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } data[index] = e; }
contains()時間複雜度O(n)
查找數組中是否包含元素e
//查找數組中是否有元素e public boolean contains(E e) { for(int i = 0; i < size; i++) { if(data[i].equals(e)) { return true; } } return false; }
find() 時間複雜度O(n)
//查找數組中元素e是否存在,返回索引,不存在返回-1 public int find(E e) { for(int i = 0; i < size; i ++) { if(data[i].equals(e)){ return i; } } return -1; }
remove() 時間複雜度O(n)
刪除指定位置的元素,而且返回刪除的元素。原理和添加一個元素相似。
// 刪除指定位置index的元素 public E remove(int index) { if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } E ret = data[index]; for(int i = index + 1; i < size; i ++) { data[i-1] = data[i]; } size --; return ret; }
在往數組中添加元素的時候,若是數組已經滿了,能夠從新建一個新的較大容量的數組,把原來的數組的元素所有放入新數組中,這樣就能夠實現擴容。減小元素到必定程度就縮容。
//向指定index處添加元素 public void add(int index, E e) { if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } if(size == data.length) { resize(2 * data.length); } for(int i = size-1; i >= index; i --) { data[i+1] = data[i]; } data[index] = e; size ++; }
// 刪除指定位置index的元素 public E remove(int index) { if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } E ret = data[index]; for(int i = index + 1; i < size; i ++) { data[i-1] = data[i]; } size --; if(size == data.length / 2) { resize(data.length / 2); } return ret; }
時間複雜度O(n)
private void resize(int newCapacity) { E[] newData = (E[]) new Object[newCapacity]; for(int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; }
實現動態數組以後,addLast操做的時間複雜度最壞是O(n)的。
假設數組容量是n,n+1次操做觸發一次擴容操做,總共2n+1次操做。平均每次addLast操做,進行兩次基本操做。這樣均攤計算,addLast操做的時間複雜度是O(1),在這個例子裏,均攤計算比最壞計算要有意義。
複雜度振盪產生:當addLast一個元素須要擴容時,複雜度是O(n),而後又立刻刪除一個元素,要縮容,時間複雜度也是O(n)。均攤的計算方法就不能用了,產生震盪。
解決辦法:讓數據元素個數減小到容積的1/4時再縮容,仍縮爲原來的一半。
// 刪除指定位置index的元素 public E remove(int index) { if(index < 0 || index > size) { throw new IllegalArgumentException("添加失敗,要求0<=index<=size"); } E ret = data[index]; for(int i = index + 1; i < size; i ++) { data[i-1] = data[i]; } size --; if(size == data.length / 4 && data.length / 2 != 0) { // Lazy方式 resize(data.length / 2); } return ret; }
代碼: https://github.com/zhang-anan/DataStructure/tree/master/src/cc/myall/demo01