什么是更有效的:System.arraycopy或Arrays.copyOf?


77

,Bloch中的toArray方法ArrayList同时使用System.arraycopyArrays.copyOf复制一个数组。

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

如何比较这两种复制方法,何时应使用哪种复制方法?


1
什么是“斑点”?为什么代码片段相关?
西罗Santilli郝海东冠状病六四事件法轮功

6
@Ciro Bloch是编写ArrayList实现的人。
失眠症

Answers:


107

不同之处在于Arrays.copyOf不仅复制元素,还创建新的数组。System.arraycopy复制到现有数组中。

这是的来源Arrays.copyOf,您可以看到它在System.arraycopy内部用于填充新数组:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    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;
}

2
很明显.... Arrays.copyOf应该创建一个新的array实例,因为我们没有将其传递给它。同时将目标传递给System.arraycopy。关于速度,显然System.arraycopy应该胜出,因为它是本机方法,最终Arrays.copyOf也调用此方法来复制数组。
Pravat Panda

4
自创建内在函数以来,Java源代码无关紧要
maaartinus

System.arraycopy具有更详细的参数,您可以为两个数组指定起始索引。
M.kazem Akhgary

公共类TestClass {公共静态void main(String [] args){字节a [] = {65,66,67,68,69,70,71,72,73,74}; System.arraycopy(a,0,a,1,a.length-1); System.out.println(新字符串(a)); }
拉贾特

上面的代码显示AABCDEFGHI,这意味着它正在创建一个新数组。
拉贾特

41

虽然System.arraycopy是本地实现的,因此可能比Java循环快1,但它并不总是像您期望的那样快。考虑以下示例:

Object[] foo = new Object[]{...};
String[] bar = new String[foo.length];

System.arraycopy(foo, 0, bar, 0, bar.length);

在这种情况下,fooandbar数组具有不同的基本类型,因此实现arraycopy必须检查复制的每个引用的类型,以确保它实际上是对String实例的引用。这比简单的C样式memcopy的数组内容要慢得多。

另一点是在引擎盖下Arrays.copyOf使用System.arraycopy。因此System.arraycopy在它的脸上不应该慢2Arrays.copyOf。但是你可以看到(转引自码以上),其Arrays.copyOf在某些情况下会使用反射来创建新的数组。因此,性能比较并不简单。

此分析有两个缺陷。

  1. 我们正在查看来自特定Java版本的实现代码。这些方法可能会更改,从而使先前关于效率的假设无效。

  2. 我们忽略了JIT编译器可以对这些方法进行一些巧妙的特殊情况优化的可能性。显然这确实是发生了Arrays.copyOf; 请参阅为什么小型阵列的Arrays.copyOf比System.arraycopy快2倍?。该方法在当前的Java实现中是“固有的”,这意味着JIT编译器将忽略Java源代码中的内容!

但是,无论哪种方式,两个版本之间的差异都是O(1)(即与数组大小无关)并且相对较小。因此,我的建议是使用使您的代码最容易阅读的版本,并且仅在概要分析告诉您这很重要时才担心哪个版本更快。


1-可能更快,但是JIT编译器也可能在优化手动编码循环方面做得很好,没有区别。


1
+1指出有时必须进行的类型检查。
Thilo

@StephenC,关于您的第一段,在某些情况下System.arrayCopy实际上会变慢吗?
Pacerier,2014年

@Pacerier-我什么都不知道。但这并非不可能。
Stephen C

1
对此响应稍晚一些,但请记住Arrays.copyOf引用了System.arrayCopy(正如在接受的答案中提到的那样),我不得不抱怨说System.arrayCopy不可能变慢。当然,这提供了使用方法的自定义实现的JVM的自定义实现。
韦恩

@Wayne-您正在假设有关JIT编译器所做的优化。可能会完全忽略您正在查看的源代码/字节码。或者它可能在将来的Java版本中做到这一点。或者该源代码在某些其他版本中可能有所不同。还是有一些聪明的优化可以在直接arraycopy调用上完成,而不能在间接调用上完成。所有这些事情都是可能的……如果不是合理的话。
斯蒂芬·C

14

如果要精确地复制数组(例如,如果要进行防御性复制),则复制数组的最有效方法可能是使用数组对象的clone()方法:

class C {
    private int[] arr;
    public C(int[] values){
        this.arr = values.clone();
    }
}

我没有费心去测试它的性能,但是它是个非常快的好机会,因为它全是本机的(分配和复制在调用中),克隆是一种特殊的JVM祝福的复制对象的方式(而且(大多数用于其他目的是邪恶的),并且可能能够采取一些“捷径”。

就个人而言,clone如果它比其他任何复制方式都慢,我仍然会使用它,因为它更容易阅读,几乎不可能在书写时弄乱。System.arrayCopy, 另一方面...


1
是的,但是它System.arrayCopy也是本地的(它专用于该任务),同时clone似乎必须处理许多条件……
Pacerier 2014年

@Pacerier“我不经常使用clone...,但是当我使用它时,我会在数组上使用它。”
gustafc 2014年

我说的 .clone()在数组上使用...。无论如何,它仍然返回一个Object,然后必须将其强制转换回数组(=其他工作)。
Pacerier 2014年

@Pacerier实际上对于所有数组类型都使用正确的返回类型进行了重载,因此您不必自己进行任何强制类型转换,并且不确定VM是否必须检查任何强制类型转换。
gustafc 2014年

好点,所以您说的是本clone()机会比本机快,System.arrayCopy因为它clone()也专用于此任务?
Pacerier,2014年

13

System.arrayCopy更快。它在系统中是因为它使用Java领域之外的直接内存副本。尽可能使用它。


1
但是System.arraycopy仅复制到现有阵列中。Arrays.copyOf还会为您创建输出数组。
Thilo 2010年

2
没错,只要您在使用System.arraycopy的情况下都使用了方便包装材料,那么它就是肉汁。
Ry4an Brase

4
而且,在某些情况下System.arrayCopy 无法使用直接内存副本。
Stephen C

12

您是否看过Sun的Arrays.copyOf()实现?

 public static int[] copyOf(int[] original, int newLength) {
    int[] copy = new int[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

可以看出,它在System.arraycopy()内部使用,因此性能是相同的。



2

这些不是实际的辩论,而是实际的结果。显然,您的选择将取决于您要复制多少数据。

byte []复制性能测试

10,000,000次迭代40b array.copyOfRange:135ms系统.arraycopy:141ms

10,000,000次迭代1000b array.copyOfRange:1861ms系统.arraycopy:2211ms

10,000,000次迭代4000b array.copyOfRange:6315ms系统.arraycopy:5251ms

1,000,000次迭代100,000b array.copyOfRange:15,198ms系统.arraycopy:14783ms


1
不知道为什么要投票,这对讨论是否有意义和有用。
网络威胁

1
我想,因为您没有提到您的JDK,OS,硬件,也没有发布您的源代码来对其进行验证。
汉斯·沃斯特

1

如果您同时查看System.arraycopy()和Array.copyOf()的源代码,则可以提高性能。

System.arraycopy()是C代码,直接在您的数组上操作,它不返回任何值,因此,它的运行速度应比Array.copyOf()快得多。但是,如果您需要创建一个新数组,或者只需要复制操作的值,则必须为此创建一个新数组,设置新数组的长度,等等。因此,您不能做返回System.arraycopy(源,0,目标,0,长度)。

对于Array.copyOf()可以做什么,它将为您创建一个新数组。您可以将Array.copyOf()的返回值分配给数组,也可以将其从方法返回,因为Array.copyOf()向您返回一个值,而不是直接在目标数组上进行操作。因此,您的代码将看起来更加简洁。但是,出于性能的考虑,Array.copyOf()是一种泛型类型方法,它不提前知道它将使用什么方法。因此,它必须调用Array.newInstance()或new Object(),然后将其强制转换为输入的数组类型。

总结一下。由于性能原因,请使用System.arraycopy()。使用Array.copyOf()获得更干净的代码。


-1
      class ArrayCopyDemo {
   public static void main(String[] args) {
    char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e',
            'i', 'n', 'a', 't', 'e', 'd' };
    char[] copyTo = new char[7];

    System.arraycopy(copyFrom, 2, copyTo, 0, 7);
    System.out.println(new String(copyTo));
}
 }

这不能回答问题。
斯蒂芬·C
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.