数组是我接触的第一个数据结构知识,对于数组,在我们编码中是十分常见的一种数据结构。下面是我学习的时候写的关于数组的一些操作,数组对我我们的后期的学习我觉得是一个铺垫性的作用,所以学好数组是必须的。
public class Array<E> {
//定义一个泛型数组
private E [] data;
//相当于一个指针,标记着数组的长度
private int size;
//初始化data的容量,但是泛型数组由于泛型擦除只能够通过Object来进行申明
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);
}
public void addFrist(E e){
add(0,e);
}
//在index插入元素,对于数组来说呢,一般添加元素就是将在index位置的元素覆盖在后一个元素上面,最后size++
//我们一般采取从后往前遍历到index的位置这样不会造成数组越界
public void add(int index,E e){
//扩容,当size等于数组长度时,进行扩容
if (size == data.length){
resize(2*data.length);
}
//参数校验
if (index < 0 || index > size){
throw new IllegalArgumentException("索引违法");
}
for (int i = size-1;i >= index ; i--) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
//取出最后一个元素
public E getLast(){
return get(size-1);
}
//取出第一个元素
public E getFirst(){
return get(0);
}
//获取index的元素,直接返回date【index】
public E get(int index){
if (index <0 || index>=size){
throw new IllegalArgumentException("索引违法");
}
return data[index];
}
//更新index位置的元素
void set(int index, E e){
if (index <0 || index>=size){
throw new IllegalArgumentException("索引违法");
}
data[index] = e;
}
//判断元素e是否存在,从前往后遍历
public boolean contains(E e){
for (int i = 0;i< size;i++){
if (data[i].equals(e))
return true;
}
return false;
}
//查找e元素所在的索引,没有返回-1
public int find(E e){
for (int i = 0;i< size;i++) {
if (data[i].equals(e))
return i;
}
return -1;
}
//删除index元素,并且返回删除元素,对于删除元素,我们首先直接将获取到index的后一个位置,
// 将后一个位置直接覆盖然后size--并让最后的元素为null
public E remove(int index){
E ret =data[index];
for (int i=index + 1;i<size;i++){
data[i-1] =data[i];
}
size--;
data[size] = null;
//因为在扩容的时候我们是2倍,在这里为了降低复杂度的震荡,我们将缩容为四分之一
if (size == data.length/4)
resize(data.length/2);
return ret;
}
public E removeFrist(){
return remove(0);
}
public E removeLast(){
return remove(size-1);
}
public void removeElement(E e){
int index=find(e);
if (index != -1)
remove(index);
}
//为了更好地看到数组的变化重载toString方法
@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();
}
//进行扩容或者缩容,首先申明一个新的数组,将旧数组的元素赋值给新数组
private void resize(int newCapacity) {
E [] newData = (E [])new Object[newCapacity];
for (int i= 0;i < size ;i++){
newData[i] = data[i];
}
data=newData;
}
public static void main(String[] args) {
Array<Integer> array=new Array<Integer>();
for (int i=0;i<10;i++){
array.addFrist(i);
}
System.out.println(array);
}
}
/**
* 对于现在的这个数组,我们已经差不多已经完成了一些常规的CURD,对于数组我觉得我想说的是
* 数组不是那么的困难,但是对于我们后面的学习是十分重要的
* 在java中的ArrayList的底层也是一个动态数组进行封装的,默认初始值为10,当然比我们现在封装的数组好很多
*/
对于我编写的这个数组中对于复杂度来讲,add方法的平均复杂度为O(N),find方法的复杂度为O(1),remove方法的平均复杂度为O(n),但是值得一提的是在在这里面做了一个处理复杂度的震荡的做法,在我的代码中的remove,add方法的扩容大小是不一样的因为在扩容的时候,我的初始容量为10,当addFirst执行完之后,再执行remove方法,那么两次复杂度就为0(n),这样是很麻烦的。所以我们扩容和缩容的边界值是不一样的,这样就巧妙的处理了复杂度的震荡。