使用System.arraycopy(...)比复制数组的for循环更好吗?

时间:2013-09-05 14:15:23

标签: java

我想创建一个新的对象数组,将两个较小的数组放在一起。

它们不能为空,但大小可能为0.

我不能在这两种方式之间做出选择:它们是等效还是效率更高(例如system.arraycopy()复制整个块)?

MyObject[] things = new MyObject[publicThings.length+privateThings.length];
System.arraycopy(publicThings, 0, things, 0, publicThings.length);
System.arraycopy(privateThings, 0, things,  publicThings.length, privateThings.length);

MyObject[] things = new MyObject[publicThings.length+privateThings.length];
for (int i = 0; i < things.length; i++) {
    if (i<publicThings.length){
        things[i] = publicThings[i]
    } else {
        things[i] = privateThings[i-publicThings.length]        
    }
}

代码外观的唯一区别是什么?

编辑:感谢关联的问题,但它们似乎有一个未解决的讨论:

如果it is not for native types:byte [],Object [],char []真的更快吗?在所有其他情况下,执行类型检查,这将是我的情况,因此将是等同的......不?

在另一个相关问题上,他们说the size matters a lot,大小&gt; 24 system.arraycopy()获胜,小于10,手动循环更好...

现在我真的很困惑。

6 个答案:

答案 0 :(得分:81)

public void testHardCopyBytes()
{
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
    byte[] out = new byte[bytes.length];
    for(int i = 0; i < out.length; i++)
    {
        out[i] = bytes[i];
    }
}

public void testArrayCopyBytes()
{
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
    byte[] out = new byte[bytes.length];
    System.arraycopy(bytes, 0, out, 0, out.length);
}

我知道JUnit测试不是最适合基准测试的,但是 testHardCopyBytes需要0.157秒才能完成 和
testArrayCopyBytes需要0.086秒才能完成
我认为这取决于虚拟机,但它看起来好像是复制内存块而不是复制单个数组元素。这绝对会提高性能。

编辑:
看起来System.arraycopy的性能已经到处都是。 当使用字符串而不是字节,并且数组很小(大小为10)时, 我得到了这些结果:

    String HC:  60306 ns
    String AC:  4812 ns
    byte HC:    4490 ns
    byte AC:    9945 ns

这是数组大小为0x1000000时的样子。看起来System.arraycopy肯定会赢得更大的数组。

    Strs HC:  51730575 ns
    Strs AC:  24033154 ns
    Bytes HC: 28521827 ns
    Bytes AC: 5264961 ns

多么奇特!

谢谢,达人,指出参考文献的复制方式不同。这使得这个问题变得更加有趣!

答案 1 :(得分:36)

Arrays.copyOf(T[], int)更容易阅读。 Internaly它使用System.arraycopy()这是本机调用。

你无法加快速度!

答案 2 :(得分:15)

这取决于虚拟机,但System.arraycopy应该为您提供最接近本机性能的功能。

我作为嵌入式系统的Java开发人员已经工作了2年(性能是一个非常重要的优先事项),并且可以使用System.arraycopy,我主要使用它/看到它在现有代码中使用。当性能成为问题时,它始终优先于循环。 如果性能不是一个大问题,我会选择循环。更容易阅读。

答案 3 :(得分:8)

我没有依赖猜测和可能过时的信息,而是使用运行了一些基准测试。事实上,Caliper附带了一些例子,其中包括一个CopyArrayBenchmark来测量这个问题!你所要做的就是运行

mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark

我的结果基于Oracle的Java HotSpot(TM)64位服务器VM,1.8.0_31-b13,运行于2010年中期的MacBook Pro(带有Intel Arrandale i7的macOS 10.11.6,8 GiB RAM)。我不相信发布原始时序数据是有用的。相反,我将用支持的可视化来总结结论。

总结:

  • 编写手动for循环以将每个元素复制到新实例化的数组中,无论是对于短数组还是长数组,都是绝对有利的。
  • Arrays.copyOf(array, array.length)array.clone()一直都很快。这两种技术的性能几乎相同;你选择哪一个是品味问题。
  • System.arraycopy(src, 0, dest, 0, src.length)几乎与Arrays.copyOf(array, array.length)array.clone()一样快,但不是那么一致。 (请参阅50000 int s的情况。)由于这一点以及通话的详细程度,如果您需要精确控制哪些元素被复制到哪里,我建议System.arraycopy()

以下是时序图:

Timings for copying arrays of length 5 Timings for copying arrays of length 500 Timings for copying arrays of length 50000

答案 4 :(得分:6)

执行像Arrays.copyOf(T[], int)这样的本机方法确实有一些开销,但并不意味着它在使用JNI执行它时速度不快。

最简单的方法是编写基准测试。

您可以检查Arrays.copyOf(T[], int)是否比正常for循环更快。

来自here的基准代码: -

public void test(int copySize, int copyCount, int testRep) {
    System.out.println("Copy size = " + copySize);
    System.out.println("Copy count = " + copyCount);
    System.out.println();
    for (int i = testRep; i > 0; --i) {
        copy(copySize, copyCount);
        loop(copySize, copyCount);
    }
    System.out.println();
}

public void copy(int copySize, int copyCount) {
    int[] src = newSrc(copySize + 1);
    int[] dst = new int[copySize + 1];
    long begin = System.nanoTime();
    for (int count = copyCount; count > 0; --count) {
        System.arraycopy(src, 1, dst, 0, copySize);
        dst[copySize] = src[copySize] + 1;
        System.arraycopy(dst, 0, src, 0, copySize);
        src[copySize] = dst[copySize];
    }
    long end = System.nanoTime();
    System.out.println("Arraycopy: " + (end - begin) / 1e9 + " s");
}

public void loop(int copySize, int copyCount) {
    int[] src = newSrc(copySize + 1);
    int[] dst = new int[copySize + 1];
    long begin = System.nanoTime();
    for (int count = copyCount; count > 0; --count) {
        for (int i = copySize - 1; i >= 0; --i) {
            dst[i] = src[i + 1];
        }
        dst[copySize] = src[copySize] + 1;
        for (int i = copySize - 1; i >= 0; --i) {
            src[i] = dst[i];
        }
        src[copySize] = dst[copySize];
    }
    long end = System.nanoTime();
    System.out.println("Man. loop: " + (end - begin) / 1e9 + " s");
}

public int[] newSrc(int arraySize) {
    int[] src = new int[arraySize];
    for (int i = arraySize - 1; i >= 0; --i) {
        src[i] = i;
    }
    return src;
}

System.arraycopy()使用JNI(Java Native Interface)复制数组(或部分数组),因此速度非常快,因为您可以确认here

答案 5 :(得分:4)

System.arraycopy()是本机调用,它直接在内存中复制操作。单个内存副本总是比你的for循环快