HOWTO · Java
在 Java 中创建动态数组
本指南教授如何在 Java 中创建动态数组。
本页内容
Java 数组创建后长度固定。若要添加超出该长度的元素,可以用一个包装普通数组的 dynamic array 类来管理它:记录当前存储的值的数量,并在空间不足时替换底层数组。下面的示例存储基本类型 int 值,只支持在末尾追加和移除最后一个值,也可以将存储空间缩减至与当前 size 相同。
在 Java 中使用自定义逻辑创建动态数组
该类管理三项状态:底层 int[]、size 和 capacity。size 表示当前存储的值的数量;capacity 是底层数组的长度。将这两个值分开记录,就能预留数组槽位,而不把这些槽位算作已存储的值。
运行此示例:
本节中的代码以及显示的标签或输出仍为英文。
import java.util.Arrays;
public class DynamicArray {
private int[] values;
private int size;
public DynamicArray() {
values = new int[2];
}
public void add(int value) {
if (size == values.length) {
int newCapacity = values.length == 0 ? 1 : values.length * 2;
values = Arrays.copyOf(values, newCapacity);
}
values[size] = value;
size++;
}
public void removeLast() {
if (size == 0) {
return;
}
size--;
values[size] = 0;
}
public void shrinkToSize() {
values = Arrays.copyOf(values, size);
}
public int size() {
return size;
}
public int capacity() {
return values.length;
}
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(values, size));
}
public static void main(String[] args) {
DynamicArray numbers = new DynamicArray();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
System.out.println(numbers + " | size=" + numbers.size()
+ " | capacity=" + numbers.capacity());
numbers.removeLast();
System.out.println(numbers + " | size=" + numbers.size()
+ " | capacity=" + numbers.capacity());
numbers.shrinkToSize();
System.out.println(numbers + " | size=" + numbers.size()
+ " | capacity=" + numbers.capacity());
}
}
输出:
[10, 20, 30, 40, 50] | size=5 | capacity=8
[10, 20, 30, 40] | size=4 | capacity=8
[10, 20, 30, 40] | size=4 | capacity=4
添加五个值后,数组中的值为 [10, 20, 30, 40, 50],大小为 5,容量为 8。调用 removeLast() 后,数组中的值为 [10, 20, 30, 40],大小为 4,容量仍为 8。调用 shrinkToSize() 后,大小和容量都为 4。
理解大小、容量与扩容
构造函数将容量设为 2,大小设为 0。每次调用 add 时,都会检查大小是否已达到容量。底层数组已满时,Arrays.copyOf 会分配一个长度为原来两倍的新数组,并将已有值复制过去。随后,新值会写入索引 size 处,大小再增加 1。
对于示例中的五次添加,容量从 2 增至 4,再增至 8。扩容时会复制已存储的值,新数组中多出的槽位则可供后续添加使用。容量表示已分配的存储空间,而不是值的数量。toString() 方法只复制数组中位于大小范围内的部分,因此多出的槽位不会显示为元素。
复制所需的时间与被复制的值的数量成正比,而且复制期间两个数组会同时占用空间。将容量加倍可以避免每次追加都要复制,但触发扩容的那次追加仍然需要复制。这个小类用于演示扩容机制;对于一般用途的集合,通常使用 Java 的 ArrayList<Integer> 更合适。
添加与移除元素
add 只支持在末尾追加,不支持在任意索引处插入。removeLast 会将大小减 1,并把腾出的槽位清为 Java 中 int 的默认值 0。int 是基本类型,不是引用,因此清空槽位并不是为了垃圾回收,也不会释放引用;这样做只是让底层数组未使用部分的状态保持可预测。当大小为 0 时调用 removeLast,对象状态不会改变。
移除元素不会自动缩小容量。如果回收多余数组槽位比为后续添加保留空间更重要,可以调用 shrinkToSize()。缩小容量时也会将值复制到替换数组中。如果大小为 0,容量会变为 0;下一次调用 add 时,容量会先增至 1,再存入新值。此实现只存储基本类型 int 值,不支持按索引访问、插入元素或移除中间元素。