Java 原生的数组拷贝方法(ArrayList 在指定位置插入元素)
·
目录
1、elementData 既是源数组又是目标数组,这是原地拷贝,不会额外占用内存。
2、拷贝的长度 s - index,是从 index 到数组有效元素末尾的所有元素个数,保证移动后不会丢失任何数据。
1、System.arraycopy 做的事,就是把 index 及其后面的元素整体向后移动一位,为新元素腾出位置。
2、这种原地拷贝的方式,保证了数组在插入时不会丢失数据,但也导致了 ArrayList 在中间位置插入的时间复杂度是 O(n)(需要移动 n 个元素)。
一、先看源码
1、ArrayList.java源码

2、System.java源码

二、先搞懂 System.arraycopy 的语法
1、它是 Java 原生的数组拷贝方法,参数顺序是:
System.arraycopy(
Object src, // 源数组
int srcPos, // 源数组中开始拷贝的位置
Object dest, // 目标数组
int destPos, // 目标数组中开始写入的位置
int length // 要拷贝的元素个数
);
2、对应ArrayList.java源代码里的参数
System.arraycopy(
elementData, // 源数组:就是 ArrayList 底层的存储数组
index, // 源起始位置:从 index 位置开始拷贝
elementData, // 目标数组:还是同一个数组(原地拷贝)
index + 1, // 目标起始位置:从 index+1 位置开始写入
s - index // 拷贝长度:从 index 到末尾,共有 s - index 个元素
);
三、它到底做了什么?
举个例子:
假设当前数组 elementData 里有 [A, B, C, D],size = 4,你要在 index=1 的位置插入元素。
执行 System.arraycopy 时:
- 1、从
index=1开始,拷贝4 - 1 = 3个元素(也就是B, C, D) - 2、把它们写入到
index+1=2的位置 - 3、执行后数组变成:
[A, B, B, C, D]
然后下一行代码 elementData[index] = element; 会把 index=1 的位置替换成新元素,比如 X,最终数组就是:[A, X, B, C, D]
四、为什么要这么做?
因为数组是连续内存结构,不能直接在中间插入元素。要在 index 位置插入新元素,必须先把 index 及其后面的所有元素整体向后移动一位,腾出 index 位置,再放入新元素。
- 这就是
ArrayList插入效率低的根本原因:移动元素需要 O (n) 时间,插入位置越靠前,需要移动的元素越多。
五、补充说明
1、elementData 既是源数组又是目标数组,这是原地拷贝,不会额外占用内存。
2、拷贝的长度 s - index,是从 index 到数组有效元素末尾的所有元素个数,保证移动后不会丢失任何数据。
六、完整示例:模拟 ArrayList 插入过程
1、代码实例
public class ArrayListInsertDemo {
public static void main(String[] args) {
// 模拟 ArrayList 底层数组
Object[] elementData = {"A", "B", "C", "D", null, null};
// 当前有效元素个数(size)
int s = 4;
// 要插入的位置
int index = 1;
// 要插入的元素
String element = "X";
System.out.println("插入前数组状态:");
printArray(elementData, s);
// 执行 System.arraycopy,把 index 及其后面的元素整体后移一位
System.arraycopy(
elementData, // 源数组
index, // 源起始位置:从 index=1 开始拷贝
elementData, // 目标数组(原地拷贝)
index + 1, // 目标起始位置:从 index+1=2 开始写入
s - index // 拷贝长度:4 - 1 = 3 个元素(B、C、D)
);
System.out.println("\n执行 arraycopy后(元素已后移,index位置空出来了):");
printArray(elementData, s);
// 把新元素放入 index 位置
elementData[index] = element;
// 有效元素个数 +1
s = s + 1;
System.out.println("\n插入完成后的数组状态:");
printArray(elementData, s);
}
// 辅助方法:打印数组有效元素
private static void printArray(Object[] array, int size) {
for (int i = 0; i < size; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}
2、运行结果
插入前数组状态:
A B C D
执行 arraycopy 后(元素已后移,index 位置空出来了):
A B B C D
插入完成后的数组状态:
A X B C D
七、过程拆解
1、插入前:
数组有效元素是 [A, B, C, D],要在 index=1 插入 X。
2、arraycopy 执行后:
B、C、D 整体后移一位,数组变成 [A, B, B, C, D],index=1 位置被空出来(此时还是原来的 B,但马上会被覆盖)。
3、放入新元素:
把 index=1 位置替换为 X,最终得到 [A, X, B, C, D]。
八、关键结论
1、System.arraycopy 做的事,就是把 index 及其后面的元素整体向后移动一位,为新元素腾出位置。
2、这种原地拷贝的方式,保证了数组在插入时不会丢失数据,但也导致了 ArrayList 在中间位置插入的时间复杂度是 O(n)(需要移动 n 个元素)。
更多推荐


所有评论(0)