java数组克隆需按类型和语义选择:基本类型用clone()最快,对象数组clone()仅为浅拷贝,深拷贝须手动处理元素,arrays.copyof对对象数组仍是浅拷贝。

Java 数组克隆不是“一种”操作,而是多种语义不同、开销各异的实现路径。选错方式,轻则逻辑出错(比如改副本影响原数组),重则性能骤降(尤其大数组或嵌套结构)。关键不在“能不能拷贝”,而在“拷不拷引用”和“走不走 JVM 底层优化”。
一、基本类型数组:Arrays.copyOf 最快,clone() 次之,循环最慢
对于 int[]、double[] 等基本类型数组,JVM 对 clone() 和 Arrays.copyOf() 都做了底层内存复制优化(如使用 System.arraycopy),实际性能非常接近。
-
arr.clone():原生方法,零额外对象创建,无泛型擦除开销,JIT 可高度内联,实测在百万级数组上比Arrays.copyOf快 5%–10% -
Arrays.copyOf(arr, arr.length):语义清晰,支持扩容/缩容,内部仍调用System.arraycopy,可读性优于clone(),但多一次方法调用和长度校验 - 手动 for 循环赋值:每次访问索引、边界检查、无向量化优化,大数组下性能下降明显(实测慢 3–5 倍)
二、对象数组:clone() 是浅拷贝,不等于安全复制
String[]、Person[] 这类对象数组调用 clone(),只复制数组容器本身 + 每个元素的引用,不复制元素对象。也就是说:
- 新旧数组是两个独立数组对象(修改数组长度/替换某位置元素互不影响)
- 但
newArr[0] == oldArr[0]为 true —— 它们指向同一个String或Person实例 - 若
Person是可变对象,修改newArr[0].setName("X"),oldArr[0]也会变
这种行为常被误认为“深拷贝”,实则是典型的浅拷贝陷阱。
三、真正深拷贝对象数组:没有银弹,需按需选型
要让 Person[] 的每个 Person 也独立,必须显式处理每个元素。常见方案效率排序(从小到大):
-
序列化反序列化(如
ObjectOutputStream):通用但最慢,涉及 I/O、反射、临时字节数组,适合一次性小数据,不适合高频调用 -
Stream + map + 构造函数:
Arrays.stream(oldArr).map(Person::new).toArray(Person[]::new),可读性好,但创建中间 Stream 和对象,GC 压力略高 -
预分配 + for 循环 + 手动克隆:先
Person[] newArr = new Person[oldArr.length],再逐个调用oldArr[i].clone()或构造函数。避免中间对象,JIT 友好,实测在千级及以上数组中最快
四、注意 Arrays.copyOf 的“假深拷贝”错觉
Arrays.copyOf(objArr, len) 对对象数组而言,仍是浅拷贝 —— 它只是把引用复制进新数组,和 clone() 行为一致。有人误以为“copy”就等于“deep”,结果在后续修改中踩坑。特别当数组元素是 ArrayList、HashMap 等集合时,改 newArr[0].add(x) 同样会同步反映到原数组对应位置。
若需集合内容也隔离,必须对每个集合元素单独执行深拷贝逻辑(例如 new ArrayList(originalList) 是浅拷贝;要深拷贝得递归遍历元素)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











