La méthode toArray
dans ArrayList
, Bloch utilise System.arraycopy
et Arrays.copyOf
pour copier un tableau.
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;
}
Comment comparer ces deux méthodes de copie et quand utiliser laquelle?
La différence est que Arrays.copyOf
ne copie pas uniquement des éléments, il crée également un nouveau tableau. System.arrayCopy
copie dans un tableau existant.
Voici la source de Arrays.copyOf
, comme vous pouvez le constater, il utilise System.arraycopy
en interne pour remplir le nouveau tableau.
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;
}
Alors que System.arraycopy
est implémenté de manière native, il est donc possible1 plus rapide qu’une boucle Java, elle n’est pas toujours aussi rapide que vous le souhaiteriez. Considérons cet exemple:
Object[] foo = new Object[]{...};
String[] bar = new String[foo.length];
System.arraycopy(foo, 0, bar, 0, bar.length);
Dans ce cas, le type de base des tableaux foo
et bar
ayant différents types de base, l'implémentation de arraycopy
doit donc vérifier le type de chaque référence copiée pour s'assurer qu'il s'agit bien d'une référence à une instance String. C'est beaucoup plus lent qu'une simple copie de type C du contenu du tableau.
L'autre point important est que Arrays.copyOf
utilise System.arraycopy
sous le capot, de sorte que les économies que vous réalisez en créant un nouveau tableau et en le remplissant vous-même à l'aide de arraycopy
seraient minimes. En supposant que c'est ce que vous essayez de faire ...
Mon conseil serait d'utiliser la version qui rend votre code plus facile à lire, et ne vous inquiétez pas de savoir lequel est le plus rapide si profiling vous dit que c'est important.
1 - C’est pourrait être plus rapide, mais il est également possible que le compilateur JIT optimise si bien une boucle de code manuel qu’il n’y a pas de différence.
Si vous voulez une copie exacte d'un tableau (par exemple, si vous voulez effectuer une copie défensive), le moyen le plus efficace de copier un tableau est probablement d'utiliser la méthode clone()
de l'objet tableau:
class C {
private int[] arr;
public C(int[] values){
this.arr = values.clone();
}
}
Je n’ai pas pris la peine de tester ses performances, mais il y a de fortes chances qu’il soit assez rapide car tout est natif (allocation et copie dans l’appel), et le clonage est en quelque sorte une manière privilégiée de copier des objets principalement mal pour d’autres buts) et est susceptible de prendre des "raccourcis".
Personnellement, j'utiliserais toujours clone
s'il était plus lent que tout autre moyen de copie, car il est plus facile à lire et presque impossible de se tromper en écrivant. System.arrayCopy
, d'autre part ...
System.arrayCopy est beaucoup plus rapide. C'est dans le système parce qu'il utilise une copie directe de la mémoire en dehors de Java. Utilisez-le quand c'est possible.
Avez-vous examiné l'implémentation de Arrays.copyOf () par Sun?
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;
}
Comme on peut le voir, il utilise System.arraycopy()
en interne, donc les performances seraient les mêmes.
System.arrayCopy
est implémenté de manière native et sera donc plus rapide que tout code Java. Je vous recommande de l'utiliser.
Au lieu de débattre, ce sont les résultats réels. Clairement, votre choix dépendra de la quantité de données que vous souhaitez copier.
octet [] test de performance de copie
10 000 000 itérations 40b array.copyOfRange: 135ms system.arraycopy: 141ms
10 000 000 itérations 1000b array.copyOfRange: 1861ms systems.arraycopy: 2211ms
10 000 000 itérations 4000b array.copyOfRange: 6315ms system.arraycopy: 5251ms
1 000 000 d'itérations 100,000b array.copyOfRange: 15198ms systems.arraycopy: 14783ms
Si vous regardez à la fois le code source de System.arraycopy () et Array.copyOf (), pour obtenir des performances.
System.arraycopy () est du code C, il agit directement sur votre tableau, il ne renvoie aucune valeur et, de ce fait, il devrait fonctionner beaucoup plus rapidement que Array.copyOf (). Néanmoins, si vous avez besoin d'un nouveau tableau pour être créé ou si vous avez juste besoin de la valeur de l'opération de copie, vous devez alors créer un nouveau tableau pour cela, définir la nouvelle longueur du tableau, etc. un retour System.arraycopy (source, 0, destination, 0, longueur).
Pour ce que Array.copyOf () peut faire alors, il crée un nouveau tableau pour vous. Vous pouvez affecter la valeur de retour de Array.copyOf () à un tableau ou la renvoyer d'une méthode car Array.copyOf () vous renvoie une valeur au lieu d'opérer directement sur votre tableau de destination. Ainsi, votre code aura l'air beaucoup plus propre. Néanmoins, pour le coût des performances, Array.copyOf () est une méthode de type générique et il ne sait pas à l'avance avec quoi il travaillera. Ainsi, il doit appeler Array.newInstance () ou new Object (), puis le convertir en type de tableau de l'entrée.
Pour résumer. Utilisez System.arraycopy () en raison des performances. Utilisez Array.copyOf () pour le code plus propre.
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));
}
}