我有一个数组 a
,它经常被更新。假设 a = [1,2,3,4,5]
。我需要创建一个确切的副本并将其命名为 b
。如果 a
更改为 [6,7,8,9,10]
,b
应该仍然是 [1,2,3,4,5]
。如何最好地实现这一点?我尝试了使用 for
循环:
for(int i=0; i<5; i++) {
b[i]=a[i];
}
但是它似乎不能正常工作。请不要使用高级术语,如深复制等,因为我不知道那是什么意思。
int[] src = new int[]{1,2,3,4,5};
int[] dest = new int[5];
System.arraycopy( src, 0, dest, 0, src.length );
然而,在大多数情况下最好使用clone():
int[] src = ...
int[] dest = src.clone();
你可以使用
int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();
同样也要。
如果您想复制:
int[] a = {1,2,3,4,5};
这是正确的方式:
int[] b = Arrays.copyOf(a, a.length);
Arrays.copyOf
在处理小型数组时可能会比a.clone()
更快。两者都可以快速复制元素,但是clone()返回Object
,所以编译器必须插入隐式转换为int[]
。您可以在字节码中看到它,类似于这样:
ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2
来自http://www.journaldev.com/753/how-to-copy-arrays-in-java的良好解释。
Java数组复制方法
Object.clone():Object类提供了clone()方法,由于Java中的数组也是一个对象,因此可以使用此方法实现完全复制。如果您想要部分复制数组,则此方法将不适合您。
System.arraycopy():System类的arraycopy()是执行数组部分复制的最佳方式。它为您提供了一种简单的方法来指定要复制的元素总数以及源数组和目标数组的索引位置。例如,System.arraycopy(source, 3, destination, 2, 5)将从源的第3个索引开始复制5个元素到目标的第2个索引处。
Arrays.copyOf():如果您想要复制数组的前几个元素或整个数组的副本,则可以使用此方法。显然,它不像System.arraycopy()那样通用,但也不会令人困惑并且易于使用。
Arrays.copyOfRange():如果您想要复制数组的一些元素,而起始索引不是0,则可以使用此方法来复制部分数组。
你可以尝试在Java中使用Arrays.copyOf()
int[] a = new int[5]{1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
您还可以使用Arrays.copyOfRange
方法。
示例:
public static void main(String[] args) {
int[] a = {1,2,3};
int[] b = Arrays.copyOfRange(a, 0, a.length);
a[0] = 5;
System.out.println(Arrays.toString(a)); // [5,2,3]
System.out.println(Arrays.toString(b)); // [1,2,3]
}
这个方法类似于Arrays.copyOf
,但是更加灵活。它们都在底层使用System.arraycopy
。
参见:
int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();
//What if array a comes as local parameter? You need to use null check:
public void someMethod(int[] a) {
if (a!=null) {
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();
}
}
我建议您不要重复造轮子,而是使用实用程序类,其中已经执行了所有必要的检查。考虑使用apache commons中的ArrayUtils。这样您的代码会更短:
public void someMethod(int[] a) {
int[] b = ArrayUtils.clone(a);
}
你可以在这里找到Apache commons。
ArrayList
,那么Arrays
就有您所需的内容。如果您查看源代码,这些绝对是获取数组副本的最佳方法。它们具有良好的防御性编程,因为如果您提供了不合逻辑的参数,System.arraycopy()
方法会抛出大量未经检查的异常。Arrays.copyOf()
,它将从第一个到第N个元素复制到新的较短数组中。public static <T> T[] copyOf(T[] original, int newLength)
2770
2771 public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772 T[] copy = ((Object)newType == (Object)Object[].class)
2773 ? (T[]) new Object[newLength]
2774 : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775 System.arraycopy(original, 0, copy, 0,
2776 Math.min(original.length, newLength));
2777 return copy;
2778 }
或者Arrays.copyOfRange()
也可以完成这个技巧:
public static <T> T[] copyOfRange(T[] original, int from, int to)
3035 public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036 int newLength = to - from;
3037 if (newLength < 0)
3038 throw new IllegalArgumentException(from + " > " + to);
3039 T[] copy = ((Object)newType == (Object)Object[].class)
3040 ? (T[]) new Object[newLength]
3041 : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042 System.arraycopy(original, from, copy, 0,
3043 Math.min(original.length - from, newLength));
3044 return copy;
3045 }
如您所见,这两个函数只是System.arraycopy
的包装函数,带有防御逻辑验证您要执行的操作是否有效。
System.arraycopy
是复制数组绝对最快的方法。
要创建一个空值安全的数组副本,你也可以使用 Object.clone()
方法和这个答案中提供的可选项。
int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);
Optional
对象只是一个空对象,引用现有的数组。关于性能影响,我认为现在说它实际上有影响还为时过早,因为这种类型的构造函数是JVM内联的好候选,因此与其他方法相比没有更多的影响。将其视为更复杂或不复杂是一种风格(函数式编程与过程式编程之间的区别,但不仅限于此)。 - Nicolas Henneaux
System.arraycopy
的javadoc,您会发现该方法需要在开始之前检查各种事项。根据静态数组类型,其中一些检查对于复制循环来说是不必要的。 - Stephen Cclone()
被证明是最快的。 - Adamsrc.clone()
更易读,出错机会比分配新数组并执行arraycopy
少得多。(并且也很快) - Brian Goetz