复制数组


345

我有一个a不断更新的数组。比方说a = [1,2,3,4,5]。我需要制作一个完全相同的副本a并称之为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]
}

但这似乎无法正常工作。请不要使用深层复制等高级术语,因为我不知道这意味着什么。

Answers:


558

您可以尝试使用System.arraycopy()

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();

9
+1用于不更换轮子。据我所知,这种解决方案可以更快地进行阵列复制。
Felipe Hummel

6
clone和arraycopy都是本地的。我希望克隆能快一点。差异并不重要。
MeBigFatGuy 2011年

5
@ Felipe,@ MeBigFatGuy-仅适用于大型阵列。对于小型阵列,由于设置开销,复制循环可能会更快。如果查看的javadoc System.arraycopy,您将看到该方法需要在启动之前检查各种情况。这些检查中的某些检查对于复制循环而言是不必要的,具体取决于静态数组类型。
Stephen C

7
@ FelipeHummel,@ MeBigFatGuy,@ StephenC-这是此处答案中提到的数组复制方法性能测试。在这种设置中,clone()结果证明是25万个元素中最快的。
亚当

6
令人失望的是,这里所有的讨论都是关于微性能问题的,这在99.999%的时间中都无关紧要。更重要的一点是,src.clone()与分配新数组并执行操作相比,此方法更具可读性,并且出错的机会要少得多arraycopy。(而且碰巧也很快。)
Brian Goetz

231

您可以使用

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

也一样


6
我只是在澄清OP的观点:“ 如果A更改为[6,7,8,9,10],则B仍应为[1,2,3,4,5] ”。OP表示他尝试使用循环,但不适用于他。
哈里·乔伊,

15
强制转换是不必要的;一个好的静态分析仪会警告它。但是克隆无疑是制作数组新副本的最佳方法。
erickson

5
@MeBigFatGuy-OP的用例需要重复复制到同一阵列,因此克隆不起作用。
Stephen C

4
@Stephen C,我没有看过。我刚刚看过他想要一份副本,然后将随后反复更新未隐藏的版本。
MeBigFatGuy 2011年

4
@MeBigFatGuy-他说:“我有一个不断更新的数组A。” 。也许我对此读得太多了,但是我认为这意味着他也在重复将A复制到B。
Stephen C

184

如果要复制以下内容:

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

62

来自http://www.journaldev.com/753/how-to-copy-arrays-in-java的不错的解释

Java数组复制方法

Object.clone():Object类提供了clone()方法,并且由于java中的数组也是Object,因此可以使用此方法实现完整的数组复制。如果要部分复制数组,则此方法不适合您。

System.arraycopy():系统类arraycopy()是对数组进行部分复制的最佳方法。它提供了一种简单的方法来指定要复制的元素总数以及源和目标数组索引位置。例如,System.arraycopy(source,3,destination,2,5)将5个元素从源复制到目标,从源的第三个索引复制到目标的第二个索引。

Arrays.copyOf():如果要复制数组的前几个元素或数组的完整副本,则可以使用此方法。显然,它不像System.arraycopy()那样通用,但也不易混淆且易于使用。

Arrays.copyOfRange():如果要复制数组的几个元素(起始索引不为0),则可以使用此方法复制部分数组。


35

我觉得所有这些“复制数组的更好方法”并不能真正解决您的问题。

你说

我尝试了类似[...]的for循环,但似乎无法正常工作?

查看该循环,没有明显的理由使其不起作用...除非:

  • 您莫名其妙地将aand b数组弄乱了(例如,ab引用相同的数组),或者
  • 您的应用程序是多线程的,并且不同的线程正在a同时读取和更新数组。

无论哪种情况,执行复制的其他方式都无法解决潜在的问题。

第一种情况的解决方法很明显。对于第二种情况,您将必须找出某种同步线程的方法。原子数组类无济于事,因为它们没有原子拷贝构造函数或克隆方法,但是使用原始互斥锁进行同步将达到目的。

(您的问题中有一些提示使我认为这确实与线程相关;例如,您的声明a一直在变化。)


2
同意..可能是真的。
MeBigFatGuy 2011年


9

所有从数组调用长度的解决方案,添加您的代码多余的null checkers考虑示例:

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


8

您也可以使用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在引擎盖下使用。


3

对于数组的空安全副本,还可以Object.clone()在此答案中提供的方法中使用可选方法。

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

尽管该解决方案过于复杂,但它也会带来内存浪费,并且如果该数组包含机密信息(例如带有密码的字节数组),还会引入安全漏洞,因为中间对象将驻留在堆上,直到可能出现垃圾回收为止。攻击者。
Weltraumschaf

1
我不同意该数组将专门用于此构造。实际上,它仅在需要时才调用克隆,并且该Optional对象只是对现有数组的引用的空对象。关于性能影响,现在说它实际上是影响还为时过早,因为这种类型的构造是在JVM内部进行内联的很好的候选者,并且其影响不比其他方法大。认为它是否更复杂是一个风格问题(函数式编程还是过程式编程,但不仅如此)。
Nicolas Henneaux

3

如果您必须使用原始数组而不是原始数组,ArrayList那么Arrays您需要什么。如果您查看源代码,那么这是获取数组副本的绝对最佳方法。它们确实具有很好的防御性编程,因为System.arraycopy()如果您向其提供不合逻辑的参数,该方法将引发许多未经检查的异常。

您可以使用其中任何Arrays.copyOf()一个将第一个Nth元素复制到新的较短数组。

public static <T> T[] copyOf(T[] original, int newLength)

复制指定的数组,截断或使用null填充(如果需要),以便副本具有指定的长度。对于在原始数组和副本中均有效的所有索引,两个数组将包含相同的值。对于副本中有效但原始索引无效的任何索引,副本将包含null。当且仅当指定长度大于原始数组的长度时,此类索引才会存在。所得数组与原始数组具有完全相同的类。

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)

将指定数组的指定范围复制到新数组中。范围的起始索引(从)必须介于零和original.length(含)之间。original [from]处的值放置在副本的初始元素中(除非from == original.length或from == to)。来自原始数组中后续元素的值将放入副本中的后续元素中。范围(to)的最终索引(必须大于或等于from)可以大于original.length,在这种情况下,会将空值放在索引大于或等于原始值的副本的所有元素中。长度-从。返回数组的长度将为--from。所得数组与原始数组具有完全相同的类。

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 是复制阵列的绝对最快的方法。


0

我对2D阵列也有类似的问题,到此结束。我正在复制主数组并更改内部数组的值,当两个副本中的值都更改时,我感到很惊讶。基本上,两个副本都是独立的,但都包含对相同内部数组的引用,我必须对内部数组进行副本复制才能获得所需的内容。

这可能不是OP的问题,但我希望它仍然可以对您有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.