一、需要实现的API
public class MyArrayList<E> {
private E[] data; //存放数据
private int size; //元素个数
private static final int INIT_CAP = 1; //初始容量
public MyArrayList(); //无参构造函数
public MyArrayList(int initCapacity); //构造函数
// *** 工具方法 ***
public int size(); //返回数组中元素个数
public boolean isEmpty(); //判断数组是否为空
private void resize(int newCap); //将数组的容量改为newCap
private void checkElementIndex(int index); //检查index索引位置是否可以存在元素
private boolean isElementIndex(int index); //函数checkElementIndex的辅助函数
private void checkPositionIndex(int index); //检查index索引位置是否可以插入元素
private boolean isPositionIndex(int index); //函数checkPositionIndex的辅助函数
// *** 增 ***
public void addLast(E e); //数组尾部添加元素e
public void add(int index, E e); //将元素e插入位置index
public void addFirst(E e); //将元素e插入数组头部
// *** 删 ***
public E removeLast(); //删除数组中最后一个元素
public E remove(int index); //删除索引为index的元素
public E removeFirst(); //删除数组中第一个元素
// *** 查 ***
public E get(int index); //查找索引为index的元素
// *** 改 ***
public E set(int index, E element); //将数组中索引为index的元素值改为element
}
二、构造函数的实现
public MyArrayList() {
this(INIT_CAP);
}
public MyArrayList(int initCapacity) {
data = (E[]) new Object[initCapacity];
size = 0;
}
三、工具函数的实现
1. size()和isEmpty()的实现
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
2. resize函数实现
private void resize(int newCap) {
//如果newCap比元素个数还小,返回
if (size > newCap) {
return;
}
//开一个容量为newCap的数组temp
E[] temp = (E[]) new Object[newCap];
//将data[0..]搬移到temp[0..] 总共搬移元素size个
System.arraycopy(data, 0, temp, 0, size);
data = temp;
}
3. 索引检查函数实现
// 检查index索引位置是否可以存在元素
private void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
// 检查index索引位置是否可以添加元素
private void checkPositionIndex(int index) {
if (!isPositionIndex(index)) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
checkElementIndex与checkPositionIndex函数的区别
checkElementIndex用于检查index索引位置是否可以存在元素
如上图,如果合法索引是0~3,如果给一个下标4,那么checkElementIndex会认为4不合法
而checkPositionIndex用于检查index索引位置是否可以添加元素
添加元素时,元素应该是“见缝插针”,所以下标4也是合法的。
四、数组增删查改的实现
1. 增
public void addLast(E e) {
int cap = data.length;
// 看data数组容量够不够
if (size == cap) {
resize(2 * cap);
}
// 在尾部插入元素
data[size] = e;
size++;
}
public void add(int index, E e) {
// 检查索引越界
checkPositionIndex(index);
int cap = data.length;
// 看data数组容量够不够
if (size == cap) {
resize(2 * cap);
}
// 搬移数据data[index...] -> data[index+1...]
System.arraycopy(data, index, data, index+1, size - index);
// 插入
data[size] = e;
size++;
}
public void addFirst(E e) {
add(0, e);
}
2. 删
public E removeLast() {
//如果数组为空,删除操作失败
if (size == 0) {
throw new NoSuchElementException();
}
int cap = data.length;
// 可以缩容,节约空间
if (size == cap / 4) {
resize(cap / 2);
}
E deletedVal = data[size - 1];
// 删除最后一个元素
data[size - 1] = null;
size--;
return deletedVal;
}
public E remove(int index) {
// 检查索引越界
checkElementIndex(index);
E deletedVal = data[index];
// 搬移数据data[index+1...] -> data[index...]
System.arraycopy(data, index+1, data, index, size-index-1);
data[size - 1] = null;
size--;
return deletedVal;
}
public E removeFirst() {
return remove(0);
}
3. 查
public E get(int index) {
// 检查索引越界
checkElementIndex(index);
return data[index];
}
4. 改
public E set(int index, E element) {
// 检查索引越界
checkElementIndex(index);
// 修改数据
E oldVal = data[index];
data[index] = element;
return oldVal;
}
总结
数组几个API的实现较为简单,要注意的是带下标index增删查改时,需先检查一下index是否合法;除此之外,在增删操作中,对数组进行自动扩容和缩容可以提高空间的利用率。