话不多说,让我们先上代码
注:构建数组所存储的元素可能为String、Integer等不同的类型,所以我们有必要设置一个泛型,让数组可以容纳我们调用时所规定的数据类型。
public class Array<E>{ private E[] data; private int size; //有参构造函数,传入数组的容量为capacity; public Array(int capacity) { data = (E[])new Object[capacity]; //类型转化 size = 0; } //无参构造函数,默认数组容量为10 public Array() { this(10); } //现在可以忽略,在之后的堆的描述中我会讲解 public Array(E[] arr) { data = (E[]) new Object[arr.length]; for (int i = 0; i < arr.length; i++){ data[i] = arr[i]; } size = arr.length; } //获取数组的容量 public int getCapacity() { return data.length; } //获取数组中元素的个数 public int getSize() { return size; } //返回数组是否为空 public boolean isEmpty() { return size == 0; } //在index索引的位置插入新元素 public void add(int index, E e) { //先对索引位置的合法性进行判断 if ( index < 0 || index > size){ throw new IllegalArgumentException("add failed, require index >= 0 and index <= size"); } //判断数组是否需要扩容 if (size == data.length) { resize( 2 * data.length); } //遍历将[indxe, size-1]的元素全部向后移动,在index位置插入新的元素 for (int i = size - 1; i >= index; i--) { data[i+1] = data[i]; } data[index] = e; //此时数组的size增加 size++; } //向所有元素后添加一个新元素 public void addLast(E e) {add(size, e);} //在所有元素前添加一个元素 public void addFirst(E e) {add(0, e); } //从数组中删除index位置的元素 public E remove(int index){ //首先判断删除index是否合理 if (index < 0 || index >= size){ throw new IllegalArgumentException("remove failed. index is illegal"); } E ret = data[index]; //遍历循环,将[index+1, size-1]范围的元素向前移动一位 for (int i = index+1; i < size; i++){ data[i-1] = data[i]; } //数组元素减少 size--; //检查此时数组是否需要缩容 if (size == data.length / 4 && data.length / 2 != 0){ resize(data.length / 2); } return ret; } //删除数组中第一个元素,并返回该元素 public E removeFirst(){ return remove(0);} //删除数组中最后一个元素,并返回该元素 public E removeLast(){ return remove(size-1);} //修改index索引位置的元素为e public void set(int index, E e){ if (index < 0 || index >= size){ throw new IllegalArgumentException("set failed. index is illegal."); } data[index] = e; } //获取index索引的位置 public E get(int index){ if (index < 0 || index >= size){ throw new IllegalArgumentException("set failed. index is illegal."); } return data[index];
} public E getFirst(){ return get(0); } public E getLast(){ return get(size-1);
}// 查找数组中是否有元素 public boolean contains( E e){ for ( int i = 0 ; i < size ; i++){ if ( data[i].equals(e)){ return true; } } return false; } // 查找数组中元素 e 所在的索引,如果不存在元素 e ,则返回 -1 ; public int find( E e) { for ( int i = 0 ; i < size ; i++) { if ( data[i].equals(i)) { return i ; } } return - 1 ; } // 从数组中删除元素 e ; public void removeElement( E e){ int index = find(e) ; if (index != - 1){ remove(index) ; } } // 将数组中的两个指定元素交换 public void swap( int i , int j){ if (i < 0 || i>= size || j < 0 || j >= size){ throw new IllegalArgumentException( "swap failed. index is illegal.") ; } E ret = data[i] ; data[i] = data[j] ; data[j] = ret ; } // 将数组空间容量跟新 private void resize( int newCapacity){ E[] newData = ( E[]) new Object[newCapacity] ; for ( int i = 0 ; i < size ; i++){ newData[i] = data[i] ; } data = newData ; }}
动态数组扩容的问题
上述代码中我们我们给出了如下假设:
在给data数组添加元素前,如果data数组中的容量和元素个数size相同,扩容为原来的2倍:
再给data数组删除元素后,如果data数组的size是capacity的1/4,并且capacity不能被2整除
如果我们单纯的将data数组中capacity和size相同时扩容(2倍),小于1/2时缩容(1/2),就会出现这样一个情况:
当size达到capacity的零界点时,数组扩容到原来的两倍,绕后如果下一个操作是一个删除操作,数组又会瞬间进行缩容,这样反复操作会增加增删操作的时间复杂度,因此我们需要设置一个范围,来给其缓冲。
此时我们数组的复杂度:
add方 : O(n/2)
addLast: O(1)
addFirst: O(n)
remove: (n/2)
removeLast: O(1);
removeFirst: O(n)