89 votes

Qu'est-ce qui est le plus efficace ? System.arraycopy ou Arrays.copyOf ?

En toArray méthode dans ArrayList Bloch utilise les deux System.arraycopy y 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 puis-je comparer ces deux méthodes de copie et quand dois-je utiliser l'une ou l'autre ?

2 votes

Qu'est-ce que "Bloch" ? Pourquoi cet extrait de code est-il pertinent ?

9 votes

@Ciro Bloch est le gars qui a écrit l'implémentation de ArrayList.

1 votes

118voto

Thilo Points 108673

La différence est que Arrays.copyOf ne se contente pas de copier des éléments, elle crée également un nouveau tableau. System.arraycopy copie dans un tableau existant.

Voici la source pour Arrays.copyOf comme vous pouvez le voir, 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;
}

2 votes

Il est évident que ....Arrays.copyOf devrait créer une nouvelle instance de tableau puisque nous ne lui en passons pas une, alors que nous passons une destination à System.arraycopy. En ce qui concerne la vitesse, il est évident que System.arraycopy devrait l'emporter puisqu'il s'agit d'une méthode native et qu'en fin de compte, Arrays.copyOf appelle également cette méthode pour copier un tableau.

4 votes

La source Java n'a pas d'importance depuis que l'intrinsèque a été créé.

0 votes

Également System.arraycopy a des paramètres plus détaillés, vous pouvez spécifier l'index de départ pour les deux tableaux.

45voto

Stephen C Points 255558

Alors que System.arraycopy est implémenté de manière native, et peut donc être 1 plus rapide qu'une boucle Java, il n'est pas toujours aussi rapide qu'on pourrait le penser. Prenons cet exemple :

Object[] foo = new Object[]{...};
String[] bar = new String[foo.length];

System.arraycopy(foo, 0, bar, 0, bar.length);

Dans ce cas, le foo y bar Les tableaux ont des types de base différents, de sorte que l'implémentation de la fonction arraycopy doit 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 de String. Cela est nettement plus lent qu'une simple méthode de style C. memcopy du contenu du tableau.

L'autre point est que Arrays.copyOf utiliza System.arraycopy sous le capot. C'est pourquoi System.arraycopy es à première vue ne devrait pas être plus lent 2 que Arrays.copyOf . Mais vous pouvez voir (à partir du code cité au-dessus de ) que Arrays.copyOf utilisera dans certains cas la réflexion pour créer le nouveau tableau. La comparaison des performances n'est donc pas simple.

Cette analyse présente quelques failles.

  1. Nous examinons le code d'implémentation d'une version spécifique de Java. Ces méthodes peuvent changer, invalidant les hypothèses précédentes sur l'efficacité.

  2. Nous ignorons la possibilité que le compilateur JIT puisse effectuer une optimisation intelligente des cas particuliers pour ces méthodes. Et il semble que cela se produise avec Arrays.copyOf ; voir Pourquoi Arrays.copyOf est-il 2 fois plus rapide que System.arraycopy pour les petits tableaux ? . Cette méthode est "intrinsèque" dans les implémentations Java de la génération actuelle, ce qui signifie que le compilateur JIT ignorera ce qui se trouve dans le code source Java !

Mais de toute façon, le différence entre les deux versions est O(1) (c'est-à-dire indépendante de la taille du tableau) et relativement petite. Par conséquent, je vous conseille d'utiliser la version qui rend votre code le plus facile à lire, et de ne vous préoccuper de la version la plus rapide que dans les cas suivants profilage vous dit que ça compte.


1 - Il podría être plus rapide, mais il est également possible que le compilateur JIT fait un si bon travail d'optimisation d'une boucle codée à la main qu'il n'y a aucune différence.

1 votes

+1 pour avoir souligné la vérification de type qui doit avoir lieu parfois.

0 votes

@StephenC, En ce qui concerne votre premier paragraphe, existe-t-il des cas où System.arrayCopy pourrait en fait être plus lent ?

0 votes

@Pacerier - Je n'en ai pas connaissance. Mais ce n'est pas impossible.

14voto

gustafc Points 13552

Si vous voulez un exact d'un tableau (par exemple, si vous voulez faire une copie défensive), la manière la plus efficace de copier un tableau est probablement d'utiliser la fonction clone() método:

class C {
    private int[] arr;
    public C(int[] values){
        this.arr = values.clone();
    }
}

Je n'ai pas pris la peine de tester les performances de ce système, mais il a de bonnes chances d'être assez rapide puisqu'il est entièrement natif (allocation et copie en appel), et que le clonage est une sorte de méthode spéciale bénie par la JVM pour copier des objets (et c'est surtout mauvais pour d'autres usages) et est susceptible de prendre quelques "raccourcis".

Personnellement, j'utiliserais toujours clone si c'était plus lent que n'importe quelle autre façon de copier, parce que c'est plus facile à lire et presque impossible de se tromper en écrivant. System.arrayCopy d'un autre côté...

1 votes

Oui, mais System.arrayCopy est également native (et elle est dédiée à cette tâche) alors que clone semblent avoir à faire face à de nombreux conditions ...

0 votes

@Pacerier "Je n'utilise pas souvent clone ... mais quand je le fais, je l'utilise sur des tableaux."

0 votes

Je parlais de que .clone() lorsqu'elle est utilisée sur des tableaux.... Dans tous les cas, elle renvoie toujours un objet qui doit ensuite être retransformé en tableau (= travail supplémentaire).

13voto

Ry4an Points 56453

System.arrayCopy est beaucoup plus rapide. Il est dans le système parce qu'il utilise une copie directe de la mémoire en dehors de la terre de Java. Utilisez-la quand c'est possible.

1 votes

Mais System.arraycopy ne fait que copier dans un tableau existant. Arrays.copyOf crée également le tableau de sortie pour vous.

2 votes

C'est vrai, et tant que vous utilisez System.arraycopy sous la couverture, tout wrapper de commodité que vous utilisez n'est qu'un bonus.

4 votes

Et, il y a des cas où System.arrayCopy ne peut pas utiliser une copie directe en mémoire.

12voto

matsev Points 6761

Avez-vous regardé l'implémentation de Arrays.copyOf() chez 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X