在这里,我们新创建一个数组类,对 Java 语言中的原始数组进行封装,使得它可以动态的扩容和缩容
Java 语言中也有类似的实现,叫 ArrayList,我们创建的数据类是它的简化版本,下面是代码实现
public class Array<E> {
private E[] data;
private int size;
public Array(int capacity) {
data = (E[]) new Object[capacity];
size = 0;
}
public Array() {
this(10);
}
public Array(E[] arr) {
data = Arrays.copyOf(arr, arr.length);
size = arr.length;
}
public int getSize() {
return size;
}
public int getCapacity() {
return data.length;
}
public boolean isEmpty() {
return size == 0;
}
/**
* 添加
*/
public void add(int index, E e) {
if (index < 0 || index > size) throw new RuntimeException("need 0 <= index <= size");
if (size == data.length) resize(data.length * 2);
System.arraycopy(data, index, data, index + 1, size - index);
data[index] = e;
size++;
}
public void addFirst(E e) {
add(0, e);
}
public void addLast(E e) {
add(size, e);
}
/**
* 删除
*/
public E remove(int index) {
if (index < 0 || index >= size) throw new RuntimeException("need 0 <= index < size");
E ret = data[index];
System.arraycopy(data, index + 1, data, index, size - index - 1);
size--;
data[size] = null;
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);
}
public void removeElement(E e) {
int index = find(e);
if (index != -1) remove(index);
}
/**
* 修改
*/
public void set(int index, E e) {
if (index < 0 || index > size) throw new RuntimeException("need 0 <= index <= size");
data[index] = e;
}
/**
* 查看
*/
public E get(int index) {
if (index < 0 || index >= size) throw new RuntimeException("need 0 <= index < size");
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;
}
public int find(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) return i;
}
return -1;
}
/**
* 动态数组
*/
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
public void swap(int a, int b) {
if (a < 0 || a >= size || b < 0 || b >= size) {
throw new IllegalArgumentException("Swap failed, require 0 <= index < size");
}
E k = data[a];
data[a] = data[b];
data[b] = k;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Array: size = %d, capacity = %d\n", size, data.length));
sb.append('[');
for (int i = 0; i < size; i++) {
sb.append(data[i]);
if (i != size - 1) sb.append(", ");
}
sb.append(']');
return sb.toString();
}
}
值得注意的是 resize 函数,resize 函数新创建了一个数组,并把原先数组里的元素挨个搬进新数组
乍一看 resize 函数的复杂度为 O(n),添加函数 add 和删除函数 remove 都会调用它,你可能会觉得我们的数据类性能极其低下
但事实上,添加函数 add 和删除函数 remove 并不是每次都会调用 resize 函数,我们来举一个列子
假设当前 capacity = 8,并且每一次添加操作都使用 addLast
我们进行 9 次 addLast 操作,只有在最后一次 addLast 操作时才会触发 resize(对前 8 次数据进行复制),总共进行了 8 + 1 + 8 = 17 次基本操作
9 次 addLast 操作,触发 resize,总共进行了 17 次基本操作,平均每次 addLast 操作,进行 2 次基本操作
假设 capacity = n,n + 1 次 addLast,触发 resize,总共进行 2n + 1 次基本操作,平均每次 addLast 操作,进行 2 次基本操作
这样均摊计算,时间复杂度是O(1)的!
在这个例子里,这样均摊计算,比计算最坏情况有意义