99

I want to create a new array of objects putting together two smaller arrays.

They can't be null, but size may be 0.

I can't chose between these two ways: are they equivalent or is one more efficient (for example system.arraycopy() copies whole chunks)?

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

or

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]        
    }
}

Is the only difference the look of the code?

EDIT: thanks for linked question, but they seem to have an unsolved discussion:

Is it truly faster if it is not for native types: byte[], Object[], char[]? in all other cases, a type check is executed, which would be my case and so would be equivalent... no?

On another linked question, they say that the size matters a lot, for size >24 system.arraycopy() wins, for smaller than 10, manual for loop is better...

Now I'm really confused.

Free-Minded
  • 5,322
  • 6
  • 50
  • 93
Daren
  • 3,337
  • 4
  • 21
  • 35
  • 17
    `arraycopy()` is a native call, which is most certainly faster. – Sotirios Delimanolis Sep 05 '13 at 14:16
  • would you be so kind as to answer and explain why? thanks! – Daren Sep 05 '13 at 14:17
  • 4
    Have you tried benchmarking the two different implementations? – Alex Sep 05 '13 at 14:17
  • 5
    Take a look here: http://stackoverflow.com/questions/2772152/why-is-system-arraycopy-native-in-java – Sotirios Delimanolis Sep 05 '13 at 14:18
  • 17
    You should choose whichever you find most readable and easiest to maintain in the future. Only when you've determined that this is the source of a bottleneck should you change your approach. – arshajii Sep 05 '13 at 14:18
  • 2
    Don't reinvent the wheel! – camickr Sep 05 '13 at 14:19
  • @Daren:- Did you check out the link which I have added inmy answer??? http://www.javaspecialists.co.za/archive/Issue124.html – Rahul Tripathi Sep 05 '13 at 14:41
  • `System.arraycopy()` is *intrinsic* – ZhongYu Sep 05 '13 at 14:44
  • @RahulTripathi I have! and thanks! but the benchamrk is flawed for my case: it uses native types and i'm unsure how it works for native types... have you seen the answer from the guy who has tried a personal benchamrk+comments? I'm still looking for a convincing answer to accept... :) – Daren Sep 05 '13 at 14:44
  • possible duplicate of [Is Java's System.arraycopy() efficient for small arrays?](http://stackoverflow.com/questions/8526907/is-javas-system-arraycopy-efficient-for-small-arrays) – Blaisorblade Feb 16 '14 at 14:42
  • What is a "fast for loop" ? – Ciro Santilli OurBigBook.com May 02 '15 at 13:55
  • @CiroSantilli六四事件法轮功 an expression, not meaning anything specific. "A [quickly written with the help of the IDE] for loop" was a bit too long. – Daren May 04 '15 at 10:21
  • @Daren thanks for the reply. I've removed the "fast" if you don't mind, as it is not a specific Java term, and I feel the question is clearer without it. – Ciro Santilli OurBigBook.com May 04 '15 at 10:25
  • @CiroSantilli六四事件法轮功 fine by me, maybe not exactly the same is transmitted, since I find a for loop faster/easier to write and read than to use *System.arraycopy(...)* but If you think others may find the term confusing as you did, then the question is better off without it. – Daren May 05 '15 at 11:41

7 Answers7

90
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!

Trent Small
  • 1,213
  • 8
  • 12
  • 2
    Thank you for your effort, but you missed to seemingly crucial points: non-native types (create a random class with anythign so the array contains references) and size... seems for smaller array size, manual for-loop is faster. Care to correct this? – Daren Sep 05 '13 at 14:32
  • 2
    Oh wow, you're right! That's interestring. Placing Strings in those arrays instead of bytes makes a huge difference: <<>> <<>> – Trent Small Sep 05 '13 at 14:36
  • What results are you getting then? also interesting would be to try with array size = 10... thanks! (I wish i had my IDE here, I'm coding without compiler). – Daren Sep 05 '13 at 14:38
  • Well now I've wrapped them in some System.nanoTime() calls and set size = 10 to see how many nanoseconds each takes. Looks like for small primitive arrays, loops are better; for references, arrayCopy is better.: <<>> <<>> <<>> <<>> – Trent Small Sep 05 '13 at 14:50
  • Very interesting results! thank you so much! can you please edit your answer to include this, and I'll gladly accept it so it stays the first for all to see... you already got my vote up. :) – Daren Sep 05 '13 at 14:54
  • If testHardCopyBytes is only ever called one single time, then likely, it's not been "warmed up" yet. The code is not yet re-compiled to optimized machine code. Micro benchmarks should always call a test routine multiple times. You'll see that the second call already runs a lot faster. – Mike Apr 08 '18 at 16:43
36

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

You can't get it faster!

Philipp Sander
  • 10,139
  • 6
  • 45
  • 78
  • it seems you can depending on quite afew things, but thanks for pointing out that fucntion which i didn't know and is indeed easier to read. :) – Daren Sep 05 '13 at 15:10
  • yes! it depends on quite a few things as @Svetoslav Tsolov said. i just wanted to point out Arrays.copyOf – Philipp Sander Sep 05 '13 at 20:21
  • 2
    `copyOf` cannot always replace `arraycopy`, but it's appropriate for this use case. – Blaisorblade Feb 16 '14 at 14:24
  • 1
    **NB** If you're looking at performance then this wont be as quick as System.arraycopy() as it requires memory allocation. If this is in a loop then repeated allocations will lead to garbage collection which will be a massive performance hit. – Will Calderwood May 29 '15 at 10:08
  • @PhilippSander I assume you're referring to my warning about memory allocation? Please explain, as I can't see how Arrays.copyOf(T[], int) can work without allocating memory. Looking at the code in Arrays.java, it's creating a new instance of the array. – Will Calderwood Jun 03 '15 at 10:07
  • 1
    @PhilippSander Just to check I wasn't being stupid I added code to copy of a 1MB array in my game loop, which almost never fires GC. With the Array.copyOf() my DVM was calling GC 5 times per second and the game became extremely laggy. I think it's safe to say there is memory allocation occurring. – Will Calderwood Jun 03 '15 at 10:32
19

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.

  • Things like array size and type (basic vs inherited) seem to affect preformance. – Daren Sep 05 '13 at 15:11
  • 2
    Yeah, it's not 'native performance' per se, which is why I said I 'mostly' use it where I can (you'll notice it mostly wins over loop copying). I guess the reason is: when it's a small array of a primitive type the 'cost-to-call' is larger than the performance boost. Using JNI can degrade performance for the same reason - the native code itself is fast, but calling it from a java process - not so much. –  Sep 05 '13 at 15:42
  • Minor correction, Arrays.copy is not JNI, it's an intrinsic. Intrinsics are much much faster than JNI. How and when the JIT compiler turns it into an intrinsic depends on the JVM/compiler used. – Nitsan Wakart Nov 12 '13 at 09:52
  • 1
    `Arrays.copy` doesn't exist, `Arrays.copyOf` is a library function. – Blaisorblade Feb 16 '14 at 14:28
19

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

200_success
  • 7,286
  • 1
  • 43
  • 74
  • 6
    Is there something weird about the int copying? It seems strange that it arraycopy would be slow on ints at large scales. – whaleberg Aug 16 '19 at 20:58
7

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;
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
DimaD
  • 69
  • 1
  • 4
6

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

Community
  • 1
  • 1
Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
  • This code uses int[] can you try it with String[] (initialized with different values: "1", "2", etc. since they are immutable – Daren Sep 05 '13 at 14:52
  • 1
    [JNI is very slow](https://stackoverflow.com/questions/7699020/what-makes-jni-calls-slow). `System.arraycopy` doesn't use it. – Chai T. Rex Apr 28 '19 at 07:49
  • No, `System.arraycopy` does not use JNI, which is only for calling third party libraries. Instead it is a native call, which means that there is a native implementation in the VM for it. – spheenik Jul 23 '19 at 12:02
4

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

Nandkumar Tekale
  • 16,024
  • 8
  • 58
  • 85
  • 3
    I've read that for non native types (any created class, like mine) it may not be so efficient... and for small sizes sizes (my case) manual for loop can be better... care to comment? – Daren Sep 05 '13 at 14:34
  • Indeed, System.arraycopy() has some overhead so for small arrays (n = ~10) a loop is actually faster – RecursiveExceptionException Jan 12 '18 at 21:01