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 仓库


原始标题:How to Copy an Array in Java

« 上一篇: Java动态代理详解