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

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

它们不能为空,但大小可能为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 []真的会更快吗?在所有其他情况下,执行类型检查,这将是我的情况,因此将等效... 不是吗?

在另一个相关的问题上,他们说,对于大小 > 24 system. arraycopy () ,the size matters a lot胜出,对于小于10,手动循环更好..。

现在我真的很困惑。

76453 次浏览

It depends on the virtual machine, but System.arraycopy should give you the closest you can get to native performance.

I've worked for 2 years as a java developer for embedded systems (where performance is a huge priority) and everywhere System.arraycopy could be used, I've mostly used it / seen it used in existing code. It's always preferred over loops when performance is an issue. If performance isn't a big issue, I'd go with the loop, though. Much easier to read.

Arrays.copyOf(T[], int) is easier to read. Internaly it uses System.arraycopy() which is a native call.

You can't get it faster!

System.arraycopy() is a native call which does copy operation directly at memory. Single memory copy would be always faster than your for loop

Executing native methods like Arrays.copyOf(T[], int) does have some overhead but it doesnot mean that it is not fast as you are executing it using JNI.

The easiest way is to write a benchmark and test.

You can check that Arrays.copyOf(T[], int) is faster than your normal for loop.

The benchmark code from 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() uses JNI (Java Native Interface) to copy an array (or parts of it), so it is blazingly fast, as you can confirm here

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

I know JUnit tests aren't really the best for benchmarking, but
testHardCopyBytes took 0.157s to complete
and
testArrayCopyBytes took 0.086s to complete.

I think it depends on the virtual machine, but it looks as if it copies blocks of memory instead of copying single array elements. This would absolutely increase performance.

EDIT:
It looks like System.arraycopy 's performance is all over the place. When Strings are used instead of bytes, and arrays are small (size 10), I get these results:

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

Here is what it looks like when arrays are at size 0x1000000. It looks like System.arraycopy definitely wins with larger arrays.

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

How peculiar!

Thanks, Daren, for pointing out that references copy differently. It made this a much more interesting problem!

Instead of relying on speculation and possibly outdated information, I ran some benchmarks using . In fact, Caliper comes with some examples, including a CopyArrayBenchmark that measures exactly this question! All you have to do is run

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

My results are based on Oracle's Java HotSpot(TM) 64-Bit Server VM, 1.8.0_31-b13, running on a mid-2010 MacBook Pro (macOS 10.11.6 with an Intel Arrandale i7, 8 GiB RAM). I don't believe that it's useful to post the raw timing data. Rather, I'll summarize the conclusions with the supporting visualizations.

In summary:

  • Writing a manual for loop to copy each element into a newly instantiated array is never advantageous, whether for short arrays or long arrays.
  • Arrays.copyOf(array, array.length) and array.clone() are both consistently fast. These two techniques are nearly identical in performance; which one you choose is a matter of taste.
  • System.arraycopy(src, 0, dest, 0, src.length) is almost as fast as Arrays.copyOf(array, array.length) and array.clone(), but not quite consistently so. (See the case for 50000 ints.) Because of that, and the verbosity of the call, I would recommend System.arraycopy() if you need fine control over which elements get copied where.

Here are the timing plots:

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

It is not possible that Arrays.copyOf is faster than System.arraycopy since this is the implementation of 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;
}