Est-il préférable d’utiliser System.arraycopy (…) qu’une boucle for pour copier des tableaux?

Je veux créer un nouveau tableau d’objects rassemblant deux tableaux plus petits.

Ils ne peuvent pas être nuls, mais la taille peut être 0.

Je ne peux pas choisir entre ces deux manières: sont-ils équivalents ou l’un des plus efficaces (par exemple, system.arraycopy () copie des morceaux entiers)?

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

ou

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

La seule différence est-elle l’aspect du code?

EDIT: merci pour la question liée, mais ils semblent avoir une discussion non résolue:

Est-ce vraiment plus rapide si it is not for native types : byte [], Object [], char []? dans tous les autres cas, une vérification de type est exécutée, ce qui serait mon cas et serait donc équivalent … non?

Sur une autre question liée, ils disent que the size matters a lot , car la taille> 24 system.arraycopy () gagne, pour les plus petits que 10, le manuel pour la boucle est mieux …

Maintenant je suis vraiment confus.

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

Je sais que les tests JUnit ne sont pas vraiment les meilleurs pour l'parsing comparative, mais
testHardCopyBytes a pris 0.157s pour terminer
et
testArrayCopyBytes a pris 0.086s à compléter.

Je pense que cela dépend de la machine virtuelle, mais il semble qu'il copie des blocs de mémoire au lieu de copier des éléments de tableau unique. Cela augmenterait absolument les performances.

MODIFIER:
On dirait que les performances de System.arraycopy sont partout. Lorsque les chaînes sont utilisées à la place des octets et que les tableaux sont petits (taille 10), j'obtiens ces résultats:

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

Voici à quoi cela ressemble lorsque les tableaux sont à la taille 0x1000000. Il semble que System.arraycopy gagne définitivement avec des tableaux plus grands.

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

Comme c'est particulier!

Merci, Daren, d’avoir souligné que les références sont différentes. Cela a rendu ce problème beaucoup plus intéressant!

Arrays.copyOf(T[], int) est plus facile à lire. En interne, il utilise System.arraycopy() qui est un appel natif.

Vous ne pouvez pas l’obtenir plus vite!

Cela dépend de la machine virtuelle, mais System.arraycopy devrait vous permettre d’obtenir le plus près possible des performances natives.

J’ai travaillé pendant deux ans en tant que développeur Java pour systèmes embarqués (où la performance est une priorité) et partout où System.arraycopy pouvait être utilisé, je l’ai surtout utilisé / vu utilisé dans du code existant. Il est toujours préférable de privilégier les boucles lorsque la performance pose problème. Si la performance n’est pas un gros problème, j’irais avec la boucle, cependant. Beaucoup plus facile à lire.

L’exécution de méthodes natives telles que Arrays.copyOf(T[], int) a un certain coût, mais cela ne signifie pas qu’il n’est pas rapide lorsque vous l’exécutez en utilisant JNI.

Le moyen le plus simple consiste à rédiger un test et un benchmark.

Vous pouvez vérifier que Arrays.copyOf(T[], int) est plus rapide que votre boucle normale.

Le code de référence ici : –

 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() utilise JNI (Java Native Interface) pour copier un tableau (ou des parties de celui-ci), il est donc extrêmement rapide, comme vous pouvez le confirmer ici

Au lieu de compter sur des spéculations et des informations éventuellement obsolètes, j’ai utilisé quelques repères en utilisant caliper . En fait, Caliper est livré avec quelques exemples, y compris un CopyArrayBenchmark qui mesure exactement cette question! Tout ce que vous avez à faire est de courir

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

Mes résultats sont basés sur le serveur virtuel VM HotSpot (64 bits) d’Oracle, version 1.8.0_31-b13, exécuté sur un MacBook Pro mi-2010 (macOS 10.11.6 avec un processeur Intel Arrandale i7, 8 Go de RAM). Je ne pense pas qu’il soit utile de publier les données de synchronisation brutes. Je vais plutôt résumer les conclusions avec les visualisations à l’appui.

En résumé:

  • Écrire un manuel for boucle pour copier chaque élément dans un tableau nouvellement instancié n’est jamais avantageux, que ce soit pour les tableaux courts ou les tableaux longs.
  • Arrays.copyOf( array , array .length) et array .clone() sont tous deux rapidement rapides. Ces deux techniques sont presque identiques en termes de performances; celui que vous choisissez est une question de goût.
  • System.arraycopy( src , 0, dest , 0, src .length) est presque aussi rapide que Arrays.copyOf( array , array .length) et array .clone() , mais ce n’est pas toujours le cas. (Voir le cas pour 50000 int s.) À cause de cela et de la verbosité de l’appel, je recommanderais System.arraycopy() si vous avez besoin d’un contrôle précis sur les éléments copiés là où.

Voici les tracés de synchronisation:

Timings pour la copie de tableaux de longueur 5 Timings pour la copie de tableaux de longueur 500 Timings pour la copie de baies de longueur 50000

Comment est-il possible que Arrays.copyOf soit plus rapide que System.arraycopy si c’est l’implémentation de 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() est un appel natif qui copie directement les opérations en mémoire. Une copie de mémoire unique serait toujours plus rapide que votre boucle for