1. Overview
1.概述
In this quick tutorial, we’ll discuss the different array copying methods in Java. Array copying may seem like a trivial task, but it can cause unexpected results and program behaviors if not done carefully.
在这个快速教程中,我们将讨论Java中不同的数组复制方法。数组复制似乎是一个微不足道的任务,但如果不小心做的话,会造成意想不到的结果和程序行为。
2. The System Class
2.系统类
Let’s start with the core Java library, System.arrayCopy(). This copies an array from a source array to a destination array, starting the copy action from the source position to the target position until the specified length.
让我们从Java核心库开始,System.arrayCopy().这是将一个数组从源数组复制到目标数组,从源位置到目标位置开始复制动作,直到指定长度。
The number of elements copied to the target array equals the specified length. It provides an easy way to copy a sub-sequence of an array to another.
复制到目标数组的元素数量等于指定的长度。它提供了一种简单的方法将一个数组的子序列复制到另一个数组。
If any of the array arguments is null, it throws a NullPointerException. If any of the integer arguments is negative or out of range, it throws an IndexOutOfBoundException.
如果任何一个数组参数是空的,它会抛出一个NullPointerException。如果任何一个整数参数是负数或超出范围,它会抛出一个IndexOutOfBoundException。
Let’s look at an example of copying a full array to another using the java.util.System class:
让我们看看一个使用java.util.System类将一个完整的数组复制到另一个数组的例子。
int[] array = {23, 43, 55};
int[] copiedArray = new int[3];
System.arraycopy(array, 0, copiedArray, 0, 3);
This method takes the following arguments: a source array, the starting position to copy from the source array, a destination array, the starting position in the destination array, and the number of elements to be copied.
该方法接受以下参数:一个源数组,从源数组中复制的起始位置,一个目标数组,目标数组中的起始位置,以及要复制的元素数量。
Let’s look at another example of copying a sub-sequence from a source array to a destination:
让我们来看看另一个从源数组复制子序列到目标数组的例子。
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. The Arrays Class
3.Arrays类
The Arrays class also offers multiple overloaded methods to copy an array to another. Internally, it uses the same approach provided by the System class that we previously examined. It mainly provides two methods, copyOf(…) and copyRangeOf(…).
Arrays类还提供了多个重载方法,以将一个数组复制到另一个数组。在内部,它使用了我们之前研究过的System类所提供的相同方法。它主要提供了两个方法:copyOf(…)和copyRangeOf(…)。
Let’s look at copyOf first:
让我们看看copyOffirst:。
int[] array = {23, 43, 55, 12};
int newLength = array.length;
int[] copiedArray = Arrays.copyOf(array, newLength);
It’s important to note that the Arrays class uses Math.min(…) for selecting the minimum of the source array length, and the value of the new length parameter to determine the size of the resulting array.
需要注意的是,Arrays类使用Math.min(…)来选择源数组长度的最小值,并使用新的长度参数值来确定结果数组的大小。
Arrays.copyOfRange() takes 2 parameters, ‘from’ and ‘to’, in addition to the source array parameter. The resulting array includes the ‘from’ index, but the ‘to’ index is excluded:
Arrays.copyOfRange()需要两个参数,’from’和’to’,此外还有源数组参数。结果数组包括’from’索引,但是‘to’索引被排除。
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]);
Both of these methods do a shallow copy of objects if applied on an array of non-primitive object types:
如果应用于非原始对象类型的数组,这两种方法都会对对象进行浅层复制。
Employee[] copiedArray = Arrays.copyOf(employees, employees.length);
employees[0].setName(employees[0].getName() + "_Changed");
assertArrayEquals(copiedArray, array);
Because the result is a shallow copy, the change in the employee name of the element of the original array caused the change in the copy array.
因为结果是一个浅层拷贝,原数组中元素的雇员名称的变化引起了拷贝数组的变化。
If we want to do a deep copy of non-primitive types, we can opt for one of the other options described in the upcoming sections.
如果我们想对非原始类型进行深度复制,我们可以选择接下来的章节中描述的其他选项之一。
4. Array Copy With Object.clone()
4.用Object.clone()进行阵列复制
Object.clone() is inherited from the Object class in an array.
Object.clone()是以数组的形式从Object类继承的。
First, we’ll copy an array of primitive types using the clone method:
首先,我们将使用克隆方法复制一个原始类型的数组。
int[] array = {23, 43, 55, 12};
int[] copiedArray = array.clone();
Here’s proof that it works:
这里有证据表明它是有效的。
assertArrayEquals(copiedArray, array);
array[0] = 9;
assertTrue(copiedArray[0] != array[0]);
The above example shows they have the same content after cloning, but they hold different references, so any change in one of them won’t affect the other one.
上面的例子显示它们在克隆后有相同的内容,但它们持有不同的引用,所以其中一个的任何变化都不会影响另一个。
On the other hand, if we clone an array of non-primitive types using the same method, then the results will be different.
另一方面,如果我们用同样的方法克隆一个非原始类型的数组,那么结果将是不同的。
It creates a shallow copy of the non-primitive type array elements, even if the enclosed object’s class implements the Cloneable interface and overrides the clone() method from the Object class.
它创建了非原始类型数组元素的浅层拷贝,即使被包围的对象的类实现了Cloneable接口并覆盖了Object类的clone()方法。
Let’s have a look at an example:
让我们来看看一个例子。
public class Address implements Cloneable {
// ...
@Override
protected Object clone() throws CloneNotSupportedException {
super.clone();
Address address = new Address();
address.setCity(this.city);
return address;
}
}
We can test our implementation by creating a new array of addresses, and invoking our clone() method:
我们可以通过创建一个新的地址数组,并调用我们的clone()方法来测试我们的实现。
Address[] addresses = createAddressArray();
Address[] copiedArray = addresses.clone();
addresses[0].setCity(addresses[0].getCity() + "_Changed");
assertArrayEquals(copiedArray, addresses);
This example shows that any change in the original or copied array will cause a change in the other one, even when the enclosed objects are Cloneable.
这个例子表明,在原始或复制的数组中的任何变化都会引起另一个数组的变化,即使所包围的对象是Cloneable。
5. Using the Stream API
5.使用Stream API
As it turns out, we can use the Stream API for copying arrays too:
事实证明,我们也可以使用Stream API来复制数组。
String[] strArray = {"orange", "red", "green'"};
String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new);
For the non-primitive types, it’ll also do a shallow copy of objects. To learn more about Java 8 Streams, we can start here.
对于非原始类型,它也会对对象进行浅层复制。要了解更多关于Java 8 Streams的信息,我们可以从这里开始。
6. External Libraries
6.外部程序库
Apache Commons 3 offers a utility class called SerializationUtils, which provides a clone(…) method. It’s very useful if we need to do a deep copy of an array of non-primitive types. It can be downloaded here, and its Maven dependency is:
Apache Commons 3提供了一个名为SerializationUtils的实用类,它提供了一个clone(..)方法。如果我们需要对一个非原始类型的数组进行深度复制,它就非常有用。它可以在这里下载,它的Maven依赖项是。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
Let’s have a look at a test case:
让我们看一下一个测试案例。
public class Employee implements Serializable {
// fields
// standard getters and setters
}
Employee[] employees = createEmployeesArray();
Employee[] copiedArray = SerializationUtils.clone(employees);
employees[0].setName(employees[0].getName() + "_Changed");
assertFalse(
copiedArray[0].getName().equals(employees[0].getName()));
This class requires that each object should implement the Serializable interface. In terms of performance, it’s slower than the clone methods written manually for each of the objects in our object graph to copy.
这个类要求每个对象都应该实现Serializable接口。就性能而言,它比为我们对象图中的每个对象手动写的克隆方法要慢。
7. Conclusion
7.结论
In this article, we discussed the various options to copy an array in Java.
在这篇文章中,我们讨论了在Java中复制一个数组的各种选项。
The method we choose to use is mainly dependent upon the exact scenario. As long as we’re using a primitive type array, we can use any of the methods offered by the System and Arrays classes. There shouldn’t be any difference in performance.
我们选择使用的方法主要取决于具体场景。只要我们使用的是原始类型的数组,我们就可以使用System和Arrays类所提供的任何方法。在性能上应该没有任何区别。
For non-primitive types, if we need to do a deep copy of an array, we can either use the SerializationUtils or add clone methods to our classes explicitly.
对于非原始类型,如果我们需要对一个数组进行深度复制,我们可以使用SerializationUtils或者明确地在我们的类中添加克隆方法。
As always, the examples shown in this article are available on over on GitHub.
一如既往,本文所展示的例子可在GitHub上找到。