数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
public class Array<E> {private E[] data;private int size;// 构造函数,传入数组的容量capacity构造Arraypublic Array(int capacity){data = (E[])new Object[capacity];size = 0;}// 无参数的构造函数,默认数组的容量capacity=10public Array(){this(10);}// 获取数组的容量public int getCapacity(){return data.length;}// 获取数组中的元素个数public int getSize(){return size;}// 返回数组是否为空public boolean isEmpty(){return size == 0;}// 在index索引的位置插入一个新元素epublic void add(int index, E e){if(size == data.length)throw new IllegalArgumentException("Add failed. Array is full.");if(index < 0 || index > size)throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");for(int i = size - 1; i >= index ; i --)data[i + 1] = data[i];data[index] = e;size ++;}// 向所有元素后添加一个新元素public void addLast(E e){add(size, e);}// 在所有元素前添加一个新元素public void addFirst(E e){add(0, e);}// 获取index索引位置的元素public E get(int index){if(index < 0 || index >= size)throw new IllegalArgumentException("Get failed. Index is illegal.");return data[index];}// 修改index索引位置的元素为epublic void set(int index, E e){if(index < 0 || index >= size)throw new IllegalArgumentException("Set failed. Index is illegal.");data[index] = e;}// 查找数组中是否有元素epublic boolean contains(E e){for(int i = 0 ; i < size ; i ++){if(data[i].equals(e))return true;}return false;}// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1public int find(E e){for(int i = 0 ; i < size ; i ++){if(data[i].equals(e))return i;}return -1;}// 从数组中删除index位置的元素, 返回删除的元素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 leakreturn ret;}// 从数组中删除第一个元素, 返回删除的元素public E removeFirst(){return remove(0);}// 从数组中删除最后一个元素, 返回删除的元素public E removeLast(){return remove(size - 1);}// 从数组中删除元素epublic void removeElement(E e){int index = find(e);if(index != -1)remove(index);}@Overridepublic 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();}}
查找
时间复杂度
数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)
查找元素快:通过索引,可以快速访问指定位置的元素。
插入
时间复杂度为 O(n),假设数组的长度为 n,现在,如果我们需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来,给新来的数据,我们需要将第 k~n 这部分的元素都顺序地往后挪一位。
删除
动态扩容
ArrayList是Java集合框架类的一员,可以称它为一个动态数组。它还有一个优势就是将数组的操作封装起来,比如增删查,以及动态扩容。
扩容
// 在index索引的位置插入一个新元素e
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);
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("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;
}
具体操作函数
// 将数组空间的容量变成newCapacity大小
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity];
for(int i = 0 ; i < size ; i ++)
newData[i] = data[i];
data = newData;
}
越界
一般数组的下标是从0开始,因此数组的第一个数下标是0 最后一个数的下标是size-1。
数组越界在 C 语言中是一种未决行为,并没有规定数组访问越界时编译器应该如何处理。因为,访问数组的本质就是访问一段连续内存,只要数组通过偏移计算得到的内存地址是可用的,那么程序就可能不会报任何错误。
而大多数语言例如JAVA中如果越界则会抛出java.lang.ArrayIndexOutOfBoundsException
