数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型数据的集合。
数组的特点:
维数组是最常用的数组,其他很多数据结构的变种也都是从一维数组来的。例如 HashMap 的拉链寻址结构,ThreadLocal 的开放寻址结构,都是从一维数组上实现的。
二维以及多维数组,在开发场景中使用到的到是不多,不过在一些算法逻辑,数学计算中到是可以使用。
在 Java 的源码中,数组是一个非常常用的数据结构,很多其他数据结构也都有数组的影子。在一些数据存放和使用的场景中,基本也都是使用 ArrayList 而不是 LinkedList
需要实现的方法
public interface PjpList {
// 添加
boolean add(E e);
// 删除
E remove(int index);
// 获取
E get(int index);
}
实现类
public class PjpArrayList implements PjpList {}
数组是一个固定的、连续的、线性的数据结构,那么想把它作为一个自动扩展容量的数组列表,则需要做一些扩展。
/**
* 默认初始化空间
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空元素
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* ArrayList 元素数组缓存区
*/
transient Object[] elementData;
/**
* List 集合元素数量
*/
private int size;
初始化分指定大小、不指定大小
public PjpArrayList() {
// 初始化 ArrayList 阶段,如果不指定大小,默认给个空的元素,当开始添加元素的时候在初始化长度
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public PjpArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 如果给定长度大于0,那么直接创建一个数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
}
}
@Override
public boolean add(E e) {
int minCapacity = size + 1;
// 判断当前容量与初始化容量,使用 Math.max 函数取最大值最为最小初始化空间
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
if (minCapacity - elementData.length > 0) {
int oldCapacity = elementData.length;
// 扩为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果是第一次扩容,扩容至初始化空间大小
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
elementData[size++] = e;
return true;
}
ArrayList 的重点离不开对 System.arraycopy 的使用,它是一个本地方法,可以让你从原数组的特定位置,迁移到新数组的指定位置和迁移数量。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
arraycopy 各个参数的含义
src: 原数组
srcPos: 原数组起始位置(从这个位置开始复制)
dest: 目标数组
destPos:目标数组粘贴的起始位置
length: 复制的个数
元素删除
public E remove(int index) {
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null; // 为了GC和我们不会再读到
return oldValue;
}
@Override
public E get(int index) {
return (E) elementData[index];
}
@Override
public String toString() {
return "PjpArrayList{" +
"elementData=" + Arrays.toString(elementData) +
", size=" + size +
'}';
}
public class test {
public static void main(String[] args) {
PjpList num =new PjpArrayList<>();
num.add("a");
num.add("b");
num.add("c");
num.remove(0);
System.out.println(num.get(1));
System.out.println(num);
num.add("d");
num.add("e");
num.add("f");
num.add("g");
num.add("h");
num.add("i");
num.add("j");
num.add("k");
num.add("l");
System.out.println("扩容");
System.out.println(num);
}
}
测试结果
⚠️注意:在添加第 11 个元素的时候,数组进行了扩容长度扩为原来的 1.5 倍(10 ->15)
c
PjpArrayList{elementData=[b, c, null, null, null, null, null, null, null, null], size=2}
扩容
PjpArrayList{elementData=[b, c, d, e, f, g, h, i, j, k, l, null, null, null, null], size=11}