发布于2026-07-08 阅读(0)
扫一扫,手机访问
在Ja va开发中,数组拷贝是一个再常见不过的操作。很多人习惯直接写个for循环来搬数据,但如果追求极致性能,System.arraycopy()往往是更好的选择。那么问题来了——它到底快在哪里?又该怎么正确使用?我们来聊聊这个话题。

从实现原理上看,System.arraycopy()是JVM内置的本地方法,底层直接调用memcpy或类似的指令,相当于把整块内存像“黑盒”一样整体搬走。而普通的for循环每读写一次元素,都要查数组长度、做索引校验——这些边界检查虽然保证了安全性,但在高频场景下就是实实在在的性能开销。
一个常见的误区是,有人把它当成“万能拷贝工具”。实际上,它不支持跨类型拷贝,比如int[]转long[]就不行,也不会做自动装箱或拆箱。这一点要特别注意。
方法签名是System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)。最容易出错的就是前两个参数的位置:源数组在前,源起始索引紧随其后。很多人写反成srcPos, src,编译虽然不会报错(IDE提示往往不明显),但运行时就直接出问题。
src和dest必须是数组引用,不能是基本类型值或null,否则会抛出NullPointerExceptionsrcPos和destPos都是从0开始的索引,传负数会直接导致IndexOutOfBoundsExceptionlength是要拷贝的元素个数,不是结束索引。如果srcPos + length > src.length,同样会抛出异常举个例子:System.arraycopy(src, 2, dest, 0, 3)表示把src[2]、src[3]、src[4]这三个元素拷贝到dest[0]到dest[2]的位置。
编译阶段只检查引用类型的兼容性,真正做元素类型校验是在运行时。比如,把String[]拷贝到Object[]是没问题的(子类转父类),但反过来就会抛出ArrayStoreException。
基本类型数组之间完全不兼容:int[]不能拷贝到long[],哪怕两个数组的长度完全一致也不行——JVM不会帮你做隐式类型转换。如果真的需要转换,可以用Arrays.stream().mapToInt()等方式,但性能损失非常明显。
int[] → int[]):最快,纯粹的内存搬运ArrayList[] → List[]):允许,但目标数组的运行时类型必须能容纳源元素ArrayStoreException对于极小的数组(比如长度不超过4个元素),System.arraycopy()本身的JNI调用开销可能会超过简单的逐元素赋值。虽然HotSpot虚拟机在某些版本会对超小拷贝做内联优化,但这不能作为可靠依赖。
还有一个更隐蔽的问题——重叠拷贝。当源数组和目标数组是同一个数组,且拷贝区间存在交集时,System.arraycopy()会按照从头到尾的顺序搬数据,中间结果很可能被覆盖。举个例子:System.arraycopy(arr, 0, arr, 1, 3)原本是想把数组向右移动一位,但实际执行时,arr[0]会先覆盖arr[1],接着用已经被修改的arr[1]去覆盖arr[2]……结果完全不对。这种情况下,推荐改用Arrays.copyOfRange(),或者手动实现倒序拷贝。
真正影响性能的关键因素其实是GC压力——避免频繁分配临时数组。如果只是为了截取数组的一部分,优先使用Arrays.copyOfRange()(它内部虽然也调用了arraycopy,但封装了创建新数组的逻辑)。如果目标数组已经存在并且可以重复利用,再直接上System.arraycopy()。
上一篇:如何在 Java 中利用 System.setErr() 将程序的异常错误信息输出重定向到特定日志流
下一篇:并发编程中的伪共享(False Sharing):分析 @Contended 注解如何通过内存对齐隔离变量缓存行
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8