目录
前言
在Java中,如果A、B是基本的数据类型,可以用赋值的方式传递值。如果A、B是两个相同类型的数组,那么A=B相当于将数组A的引用传递给数组B;如果数组A发生改变,那么引用数组B也要发生改变。
在 Java 中实现数组复制有 5 种方法:
- 【1】Arrays 类的 copyOf() 方法
- 【2】System 类的 arraycopy() 方法
- 【3】Arrays类的copyOfRange() 方法
- 【4】Object 类的 clone() 方法
- 【5】for循环
JDK版本:
- JDK_1.8.0_181
接下来,本节将逐一分析上面这几种数组复制种方法的源码,并对各自执行效率进行对比。
1、Arrays.copyOf()
首先,进入Arrays类中定义的copyOf()源码:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
从源码定义可以看出,copyOf()方法实际上调用的是System.arraycopy()方法。
- 语法格式:
- Arrays.copyOf(dataType[] srcArray, int length);
- 参数解释:
- srcArray 表示要进行复制的数组;
- length 表示复制后的新数组的长度
OK,进入arraycopy()源码一探究竟。
2、System.arraycopy()
arraycopy() 方法位于 java.lang.System 类中。看下它在源码中是如何定义的:
//从指定的源数组开始复制数组指定位置,指向目标数组的指定位置。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
arraycopy()方法是一个本地方法,方法对应的实现不在当前文件里,而是在其他语言实现的的文件的,比如C、C++中。
- 语法格式:
- System.arraycopy(dataType[] srcArray, int srcIndex, int destArray, int destIndex, int length);
- 参数解释:
- src表示源数组;
- srcPos表示源数组中的起始索引(包含);
- dest表示目标数组;
- destPos表示目标数组中的起始索引(不包含);
- length表示要复制的源数组长度。
- 注意:
- length + srcIndex的长度须 <= 源数组长度srcArray.length
- length+destIndex的长度须 <= 目标数组长度destArray.length
3、Arrays.copyOfRange()
copyOfRange() 方法位于Arrays类中。看下它在源码中是如何定义的:
public static <T> T[] copyOfRange(T[] original, int from, int to) {
return copyOfRange(original, from, to, (Class<? extends T[]>) original.getClass());
}
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}
从源码中看,copyOfRange()方法实际上也是调用的是System.arraycopy()方法。
- 语法格式:
- Arrays.copyOfRange(dataType[] srcArray, int fromIndex, int enIndex);
- 参数解释:
- srcArray表示源数组;
- fromIndex表示开始数组复制的起始索引,目标数组中将包含起始索引对应的元素,范围:0 ~ srcArray.length ;
- endIndex表示开始数组复制结束索引,目标数组中将不包含终止索引对应的元素,范围:大于fromIndex;
- 注意:
- 结束索引endIndex可以大于 srcArray.length,如果大于 srcArray.length,则目标数组中使用默认值填充。
这个方法相当于将源数组的一部分复制为一个新的数组,最终返回新的数组。
4、Object.clone()
clone() 方法也可以实现复制数组。该方法是类 Object 中的方法,可以创建一个有单独内存空间的对象。因为数组也是一个 Object 类,因此也可以使用数组对象的 clone() 方法来复制数组。
看源码定义:
protected native Object clone() throws CloneNotSupportedException;
clone() 方法是一个本地方法,返回值Object 类型,要使用强制类型转换为适当的类型。
- 语法格式:
- srcArray.clone();
5、for循环
for循环狂野粗暴,可以说能够贯穿我们整个职业生涯,不再赘述。
6、执行效率比较
举个栗子:
public class ArrayCopyTest {
public static void test_SystemArrayCopy(String[] srcArray) {
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
System.arraycopy(srcArray, 0, destArray, 0, srcArray.length);
long end = System.nanoTime();
System.out.println("System.arraycopy()方法耗时:" + (end - start) + " ms");
}
public static void test_ArraysCopyOf(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
destArray = Arrays.copyOf(srcArray, 0);
long end = System.nanoTime();
System.out.println("Arrays.copyOf方法耗时:" + (end - start) + " ms");
}
public static void test_CopyOfRange(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
destArray = Arrays.copyOfRange(srcArray,0,srcArray.length);
long end = System.nanoTime();
System.out.println("Arrays.copyOfRange()方法耗时:" + (end - start) + " ms");
}
public static void test_Clone(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
destArray = srcArray.clone();
long end = System.nanoTime();
System.out.println("Object.clone()方法耗时:" + (end - start) + " ms");
}
public static void test_For(String[] srcArray){
long start = System.nanoTime();
String[] destArray = new String[srcArray.length];
for (int i = 0; i < srcArray.length; i++) {
destArray[i] = srcArray[i];
}
long end = System.nanoTime();
System.out.println("for循环复制数组耗时:" + (end - start) + " ms");
}
public static void main(String[] args) {
String[] srcArray = new String[100];
System.out.println("源数组长度:" + srcArray.length);
test_SystemArrayCopy(srcArray);
test_ArraysCopyOf(srcArray);
test_CopyOfRange(srcArray);
test_Clone(srcArray);
test_For(srcArray);
}
}
通过不断地改变目标数组srcArray的长度,我们来观察一下各自的执行效率(耗时)情况:
- 当srcArray.length = 100时
效率:for循环 > System.arraycopy() >Arrays.copyOfRange() > clone() > Arrays.copyOf()
- 当srcArray.length = 200时
效率:System.arraycopy() > for循环 > clone() > Arrays.copyOfRange() > Arrays.copyOf()
- 当srcArray.length = 1000时
效率:System.arraycopy() > clone() > Arrays.copyOfRange() > for循环 > Arrays.copyOf()
- 当srcArray.length = 2000时
效率:System.arraycopy() > clone() > Arrays.copyOfRange() > for循环 > Arrays.copyOf()
- 当srcArray.length = 10000时
效率:System.arraycopy() > clone() > Arrays.copyOfRange() > Arrays.copyOf() > for循环
- 当srcArray.length = 20000时
效率:System.arraycopy() > Arrays.copyOf() > clone() > Arrays.copyOfRange() > for循环
由上表测试结果可知,随着数组长度不断增多,System.arraycopy()方法的效率最高,而for循环的效率则随着数组长度越来越大情况下,其执行效率有点刺眼睛。
7、小结
本节首先介绍了数组复制的几种方式;然后针对这几种方式的源码逐一进行解读,并简单介绍了其语法格式和参数释义;最后,通过针对这几种数组复制方式给出了对应测试栗子不断修改数组长度的方式对这几种数组复制方法的效率进行比较。
- 数组复制的几种方式:
- 【1】Arrays 类的 copyOf() 方法
- 【2】System 类的 arraycopy() 方法
- 【3】Arrays类的copyOfRange() 方法
- 【4】Object 类的 clone() 方法
- 【5】for循环
- 数组复制的几种方法对应源码解读及语法格式简述。
- 测试栗子,依次对各个方法进行比较,可知数组长度较小时,for循环执行效率显著;当数组长度逐渐增大,甚至其长度过分大的时候,System.arraycopy()的效率优势较为显著。
- 因此,在平时项目开发中,我们可以结合项目实地场景,及所需操作数组的长度,灵活地选择数组复制方式,进而对于提升程序性能有意外的效果。