1. 概述
本文将深入探讨 Java 中数组复制的多种实现方式。数组复制看似简单,但若处理不当,极易引发意外结果和程序行为异常。我们将从基础到进阶,全面解析各种复制方案的适用场景和注意事项。
2. System 类方案
Java 核心库提供了 System.arraycopy()
方法,这是最底层的数组复制实现。它支持从源数组向目标数组复制指定长度的元素,并允许指定源数组和目标数组的起始位置。
⚠️ 使用时需注意:
- 若数组参数为
null
,抛出NullPointerException
- 若位置或长度参数非法(负值或越界),抛出
IndexOutOfBoundsException
完整数组复制示例:
int[] array = {23, 43, 55};
int[] copiedArray = new int[3];
System.arraycopy(array, 0, copiedArray, 0, 3);
子数组复制示例:
int[] array = {23, 43, 55, 12, 65, 88, 92};
int[] copiedArray = new int[3];
System.arraycopy(array, 2, copiedArray, 0, 3);
验证结果:
assertTrue(3 == copiedArray.length);
assertTrue(copiedArray[0] == array[2]);
assertTrue(copiedArray[1] == array[3]);
assertTrue(copiedArray[2] == array[4]);
3. Arrays 类方案
Arrays
类提供了两种便捷的数组复制方法:copyOf()
和 copyOfRange()
,其底层实际调用 System.arraycopy()
实现。
3.1 copyOf 方法
int[] array = {23, 43, 55, 12};
int newLength = array.length;
int[] copiedArray = Arrays.copyOf(array, newLength);
✅ 内部使用 Math.min()
自动处理长度差异,确保不会越界
3.2 copyOfRange 方法
int[] array = {23, 43, 55, 12, 65, 88, 92};
int[] copiedArray = Arrays.copyOfRange(array, 1, 4);
验证结果:
assertTrue(3 == copiedArray.length);
assertTrue(copiedArray[0] == array[1]);
assertTrue(copiedArray[1] == array[2]);
assertTrue(copiedArray[2] == array[3]);
⚠️ 重要提示:对于对象数组,这两种方法执行的都是浅拷贝:
Employee[] copiedArray = Arrays.copyOf(employees, employees.length);
employees[0].setName(employees[0].getName() + "_Changed");
assertArrayEquals(copiedArray, array); // 修改原数组会影响副本
若需深拷贝,需考虑后续方案。
4. Object.clone() 方案
数组类型天然继承 Object
类的 clone()
方法。
4.1 基本类型数组
int[] array = {23, 43, 55, 12};
int[] copiedArray = array.clone();
验证独立性:
assertArrayEquals(copiedArray, array);
array[0] = 9;
assertTrue(copiedArray[0] != array[0]); // 修改原数组不影响副本
4.2 对象数组
即使对象实现了 Cloneable
接口,数组 clone()
仍是浅拷贝:
public class Address implements Cloneable {
// ...
@Override
protected Object clone() throws CloneNotSupportedException {
super.clone();
Address address = new Address();
address.setCity(this.city);
return address;
}
}
测试用例:
Address[] addresses = createAddressArray();
Address[] copiedArray = addresses.clone();
addresses[0].setCity(addresses[0].getCity() + "_Changed");
assertArrayEquals(copiedArray, addresses); // 修改原数组仍影响副本
❌ 即使元素对象可克隆,数组克隆仍无法实现深拷贝。
5. Stream API 方案
Java 8+ 可利用 Stream 实现数组复制:
String[] strArray = {"orange", "red", "green'"};
String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new);
⚠️ 同样执行浅拷贝,仅适用于简单场景。
6. 外部库方案
Apache Commons Lang3 提供了强大的深拷贝工具:
Maven 依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
使用示例:
public class Employee implements Serializable {
// 字段
// 标准 getter/setter
}
Employee[] employees = createEmployeesArray();
Employee[] copiedArray = SerializationUtils.clone(employees);
验证深拷贝效果:
employees[0].setName(employees[0].getName() + "_Changed");
assertFalse(
copiedArray[0].getName().equals(employees[0].getName())); // 副本独立
✅ 优势:
- 实现真正的深拷贝
- 无需手动编写克隆逻辑
⚠️ 注意事项:
- 所有元素类必须实现
Serializable
- 性能低于手动克隆方案
7. 总结
选择数组复制方案需根据具体场景权衡:
场景 | 推荐方案 | 拷贝类型 | 性能 |
---|---|---|---|
基本类型数组 | System.arraycopy /Arrays.copyOf |
值拷贝 | ⭐⭐⭐⭐⭐ |
对象数组浅拷贝 | Arrays.copyOf /clone() |
浅拷贝 | ⭐⭐⭐⭐ |
对象数组深拷贝 | SerializationUtils.clone |
深拷贝 | ⭐⭐ |
关键决策点:
- 基本类型数组:任意方案均可,性能无差异
- 对象数组浅拷贝:优先
Arrays.copyOf
(代码简洁) - 对象数组深拷贝:使用
SerializationUtils
或手动实现克隆
完整示例代码可查阅 GitHub 仓库。