需要注意的几个点:
1.泛型 使用泛型声明数组时 必须用Object类 然后强转为泛型
public Array(int capacity){
data = (E[])new Object[capacity];
size=0;
}
2.动态数组
扩容 缩容 定义私有的resize函数。体现了Java的封装性
//动态数组
private void resize(int newCapactiy) {
E[] newData = (E[]) new Object[newCapactiy];
for(int i=0;i<size;i++){
newData[i] = data[i];
}
data = newData;
}
在移除元素时 当数组容量size小于等于数组长度的一半时,进行缩容。
//删除指定位置元素e 返回删除的元素
public E remove(int index){
if(index<0 || index>size ){
throw new IllegalArgumentException("remove failed. Index is Illegal.");
}
E ret = data[index];
for(int i = index + 1 ; i < size ;i ++){
data[i-1] = data[i];
}
size--;
data[size] = null; //loitering objects != memory leak 闲逛的对象 != 内存泄漏
//数组缩容
if(size ==data.length/2)
resize(data.length/2);
return ret;
}
完整代码
public class Array<E> {
private E[] data;
private int size;//数组中有效元素
/**
* 构造函数 传入数组的容量capacity构造Array
* @param capacity
*/
public Array(int capacity){
data = (E[])new Object[capacity];
size=0;
}
//无参构造函数 默认数组容量是10
public Array(){
this(10);
}
public int getSize(){
return size;
}
public int getCapacity(){
return data.length;
}
//返回数组是否为空
public boolean isEmpty(){
return size==0;
}
public void addLast(E e){
add(size,e);
// if(size==data.length){
// throw new IllegalArgumentException("AddLast failed. Array is full.");
// }
// data[size] = e;
// size++;
}
public void addFirst(E e){
add(0,e);
}
//在index位置 插入新的元素e
public void add(int index, E e) throws IllegalArgumentException {
if(index<0 || index>size ){
throw new IllegalArgumentException("Add failed. Require index>=0 and index<=size.");
}
if(size ==data.length){
// throw new IllegalArgumentException("Add failed. Array is full");
resize(2*data.length);
}
for(int i=size-1;i>=index;i--){
data[i+1] = data[i];
}
data[index] = e;
size++;
}
//查找数组中是否有元素e
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(e)){
return i;
}
}
return -1;
}
//删除指定位置元素e 返回删除的元素
public E remove(int index){
if(index<0 || index>size ){
throw new IllegalArgumentException("remove failed. Index is Illegal.");
}
E ret = data[index];
for(int i = index + 1 ; i < size ;i ++){
data[i-1] = data[i];
}
size--;
data[size] = null; //loitering objects != memory leak 闲逛的对象 != 内存泄漏
//数组缩容
if(size ==data.length/2)
resize(data.length/2);
return ret;
}
public E removeFirst(){
return remove(0);
}
public E removeLast(){
return remove(size-1);
}
//从数组中删除元素e
public boolean removeElement(E e){
int index = find(e);
if(index!=-1){
remove(index);
return true;
}
return false;
}
//获取索引为index的元素
public E get(int index){
if(index<=0 || index>=size) throw new IllegalArgumentException("Get Failed. Index is illegal.");
return data[index];
}
//修改index索引位置的元素为e
public void set(int index,E e){
if(index<=0 || index>=size) throw new IllegalArgumentException("Get Failed. Index is illegal.");
data[index] = e;
}
//动态数组
private void resize(int newCapactiy) {
E[] newData = (E[]) new Object[newCapactiy];
for(int i=0;i<size;i++){
newData[i] = data[i];
}
data = newData;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d, capacity = %d\n" ,size ,data.length));
res.append("[");
for(int i=0;i<size;i++){
res.append(data[i]);
if(i!=size-1){
res.append(", ");
}
}
res.append("]");
return res.toString();
}
}
动态数组时间复杂度:
- 添加操作
addLast(e) O(1) 最坏情况需要resize 时间复杂度则变为O(n)
addFirst(e) O(n)
add(index,e) O(n/2)---->O(n) resize(e) O(n)
- 删除操作
removeLast(e) O(1)
removeFirst(e) O(n)
remvoe(index,e) O(n/2)---->O(n) resize(e) O(n)
- 修改操作
set(index,e) O(1) 支持随机访问
- 查找操作
get(index) O(1)
contains(e) O(n)
find(e) O(n)
- 总结:
- 增: O(n)
- 删: O(n)
- 改: 已知索引是O(1); 未知索引是O(n)
- 查: 已知索引是O(1); 未知索引是O(n)
PPPPPS:resize的复杂度分析 addLast的均摊复杂度为O(1)
假设当前capacity = 8 并且每一次添加操作都使用addLast
1 1 1 1 1 1 1 1 8+1 ( 前面八次添加满后 使用resize 赋值八次 然后再添加一次 )
9次addLast操作,触发了1次resize , 总共进行了17次基本操作(给一个空间附上一个值 9次添加操作 和 8次元素转移的操作)
所以,平均每次addLast操作,进行2次基本操作(17/9)...........
推广到n........
假设当前capacity = n 并且每一次添加操作都使用addLast,
n次 addLast 操作 触发resize (n+1次基本操作) 所以总共进行 2n+1 次基本操作
所以,平均每次addLast操作,进行了2次基本操作( 2n+1 / n+1).......................不可能每次addLast操作都会触发resize,综合考虑 平均每次addLast操作,进行了2次基本操作。 这样均摊计算,addLast操作时间复杂度是O(1) 级别的 !!
同理 removeLast操作 均摊时间复杂度也为O(1)
PPPPPPPPPPPS:复杂度震荡
当我们同时看addLast 和 removeLast操作:
若一个数组的capacity = n 且size=n 此时再调用addLast 显然需要扩容resize O(n) 若马上进行removeLast操作 又需要缩容resize O(n) 再addLast 再removeLast ............. 这样每次addLast removeLast 都会触发resize 就会造成复杂度震荡达到了O(n)这个级别!!!
造成这个问题的原因在于: removeLast时 resize过于着急 (Eager策略)
解决方案:Lazy策略
例如: 若数组capacity=n size=n 若进行addLast操作 则会触发resize 操作 此时capacity = 2*n size = n+1 此时若进行removeLast操作时 size--; 但 当 size == capactiy / 4 时 再触发resize操作(将 capacity == capacity / 2) 则可以解决。 .