Java数组克隆关键在于方式、时机与深度选择:System.arraycopy最快但需手动分配;Arrays.copyOf次之,简洁但有开销;clone()语义清晰却慢9倍;for循环仅适合极小数组;对象数组默认浅拷贝易引发数据串扰,需按需深拷贝或改用不可变对象。

Java 数组克隆不是“要不要做”,而是“怎么克、何时克、克多深”。在高性能场景中,盲目调用 clone() 往往带来明显性能损耗——尤其对中大型数组或高频调用路径。真正影响吞吐与延迟的,从来不是克隆动作本身,而是方式选择、内存分配时机和拷贝语义是否匹配业务需求。
克隆方式选错,速度差十倍
四种常用方式在真实负载下表现差异显著:
- System.arraycopy():JVM 底层优化,绕过边界检查、支持 SIMD 向量化,是中大型数组(≥ 数千元素)最快选择;需手动创建目标数组,类型与长度必须严格匹配。
-
Arrays.copyOf():内部调用
arraycopy,但额外执行Math.min和新数组分配,比前者慢约 10–20%;胜在简洁,支持自动扩容/截断,适合配置类、模板数组等写少读多场景。 -
clone():语义清晰,一行搞定,但实测性能仅为
arraycopy的 1/9–1/10;对对象数组仍是浅拷贝,且无法控制数组长度,不适合性能敏感路径。 -
for 循环:小数组(≤ 4 元素)可能略快于
clone();唯一支持边复制边转换(如过滤、类型映射、单位换算),但随长度增长性能断崖式下滑,不建议用于 > 100 元素场景。
浅拷贝陷阱:对象数组改着改着就串了
基本类型数组(int[]、byte[])用任何方式克隆都是安全的独立副本;但对象数组(String[]、Person[])默认只复制引用——两个数组指向同一堆内对象实例。
例如:
立即学习“Java免费学习笔记(深入)”;
Person[] original = {new Person("Alice")};
Person[] cloned = original.clone();
cloned[0].setName("Bob"); // original[0] 的 name 也变成 "Bob"
若真需隔离,不应依赖 clone(),而应评估:
- 能否改用不可变对象(
String、LocalDateTime、Record)避免修改; - 是否只需构造新对象替代原引用(
cloned[i] = new Person(original[i])); - 深拷贝代价远高于数组复制本身,仅在必要时考虑 JSON 序列化或手写递归逻辑。
克隆时机比动作本身更关键
很多“慢”并非来自复制,而是克隆被放在了错误位置:
- 循环内反复克隆同一模板数组(如每次请求 clone 缓存数据),应改为启动时预克隆一次,复用副本;
- 百万级
float[]直接clone()瞬间占用几十 MB 堆内存,可改用分块处理、MemorySegment视图或Arrays.spliterator()流式计算; - Android 中
RecyclerView数据更新,全量clone()列表不如用DiffUtil计算最小变更集,仅提交差异项。
别让“看起来安全”掩盖真实成本
一些常见误用正在 silently 拖垮系统:
- 把
ArrayList.toArray().clone()当成深拷贝——实际仍是对象引用复制; - 在日志或监控模块中对大数组无条件
clone()再打印,导致 GC 压力陡增; - 为“线程安全”对每个入参数组都 clone,却忽略多数场景下参数本就是临时构造或只读使用。
不复杂但容易忽略。



















