214 votes

.toArray(new MyClass[0]) ou .toArray(new MyClass[myList.size()]) ?

En supposant que j'ai une ArrayList

ArrayList myList;

Et que je veux appeler toArray, y a-t-il une raison de performance à utiliser

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

plutôt que

MyClass[] arr = myList.toArray(new MyClass[0]);

?

Je préfère le deuxième style, car il est moins verbeux, et j'ai supposé que le compilateur se chargerait de s'assurer que le tableau vide ne serait pas réellement créé, mais je me demandais si c'était vrai.

Bien sûr, dans 99% des cas, cela ne fait pas de différence d'une manière ou d'une autre, mais j'aimerais garder un style cohérent entre mon code normal et mes boucles internes optimisées...

9 votes

Il semble que la question ait maintenant été résolue dans un nouveau billet de blog par Aleksey Shipilv, Les tableaux de la sagesse des anciens !

7 votes

Du billet de blog : "En résumé : toArray(new T[0]) semble plus rapide, plus sûr et plus propre contractuellement, et devrait donc être le choix par défaut maintenant."

122voto

Georgi Points 2153

À partir de ArrayList en Java 5, le tableau sera déjà rempli s'il a la bonne taille (ou plus grande). En conséquence

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

créera un objet tableau, le remplira et le renverra à "arr". En revanche

MyClass[] arr = myList.toArray(new MyClass[0]);

créera deux tableaux. Le second est un tableau de MyClass de longueur 0. Ainsi, il y a une création d'objet pour un objet qui sera jeté immédiatement. À en juger par le code source, le compilateur / JIT ne peut pas optimiser celui-ci de sorte qu'il ne soit pas créé. De plus, l'utilisation de l'objet de longueur zéro entraîne des conversions implicites dans la méthode toArray().

Voir la source de ArrayList.toArray():

public  T[] toArray(T[] a) {
    if (a.length < size)
        // Créer un nouveau tableau de type d'exécution de a, mais avec mes éléments :
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

Utilisez la première méthode pour ne créer qu'un seul objet et éviter les conversions (implicitement mais néanmoins coûteuses).

1 votes

Deux commentaires, qui pourraient intéresser quelqu'un : 1) LinkedList.toArray(T[] a) est encore plus lent (utilise la réflexion : Array.newInstance) et plus complexe; 2) D'un autre côté, dans la version JDK7, j'ai été très surpris de constater que Array.newInstance, habituellement très lent, fonctionne presque aussi rapidement que la création d'un tableau habituelle!

1 votes

@ktaria size est un membre privé de ArrayList, spécifiant ****suprise**** la taille. Voir Code source de ArrayList

3 votes

Deviner les performances sans benchmarks ne fonctionne que dans des cas triviaux. En fait, nouvelle Classe[0] est plus rapide: shipilev.net/blog/2016/arrays-wisdom-ancients

18voto

Les JVM modernes optimisent la construction réfléchie de tableaux dans ce cas, donc la différence de performance est minime. Nommer la collection deux fois dans un tel code d'amorçage n'est pas une bonne idée, donc je éviterais la première méthode. Un autre avantage de la seconde est qu'elle fonctionne avec des collections synchronisées et concurrentes. Si vous souhaitez optimiser, réutilisez le tableau vide (les tableaux vides sont immuables et peuvent être partagés), ou utilisez un profileur(!).

2 votes

En votant «réutiliser le tableau vide», car c'est un compromis entre lisibilité et performance potentielle qui mérite d'être pris en considération. Passer un argument déclaré private static final MyClass[] EMPTY_MY_CLASS_ARRAY = new MyClass[0] n'empêche pas que le tableau retourné soit construit par réflexion, mais empêche qu'un tableau supplémentaire soit construit à chaque fois.

0 votes

Machael a raison, si vous utilisez un tableau de longueur nulle il n'y a pas de solution : (T[])java.lang.reflect.Array.newInstance(a.getClass().getCom‌​ponentType(), size); qui serait superflu si la taille était >= actualSize (JDK7)

0 votes

Si vous pouvez donner une citation pour "les JVM modernes optimisent la construction réfléchie de tableaux dans ce cas", je voterai volontiers pour cette réponse.

3voto

Dave Cheney Points 2195

ToArray vérifie que le tableau passé est de la bonne taille (c'est-à-dire suffisamment grand pour contenir les éléments de votre liste) et, le cas échéant, l'utilise. Par conséquent, si la taille du tableau fourni est inférieure à celle requise, un nouveau tableau sera créé de manière réflexive.

Dans votre cas, un tableau de taille zéro est immuable, il pourrait donc être élevé en tant que variable statique finale, ce qui rendrait votre code un peu plus propre en évitant de créer le tableau à chaque invocation. Un nouveau tableau sera de toute façon créé à l'intérieur de la méthode, il s'agit donc d'une optimisation de lisibilité.

On pourrait arguer que la version la plus rapide est de passer le tableau de la bonne taille, mais sauf si vous pouvez prouver que ce code est un goulot d'étranglement en termes de performances, privilégiez la lisibilité à la performance d'exécution jusqu'à preuve du contraire.

2voto

Panagiotis Korros Points 3073

Le premier cas est plus efficace.

Cela est dû au fait que dans le deuxième cas :

MyClass[] arr = myList.toArray(new MyClass[0]);

le runtime crée réellement un tableau vide (de taille zéro) et ensuite, à l'intérieur de la méthode toArray, crée un autre tableau pour s'adapter aux données réelles. Cette création est effectuée en utilisant la réflexion en utilisant le code suivant (extraite de jdk1.5.0_10) :

public  T[] toArray(T[] a) {
    if (a.length < size)
        a = (T[])java.lang.reflect.Array.
    newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

En utilisant la première forme, vous évitez la création d'un deuxième tableau et évitez également le code de réflexion.

0 votes

ToArray() n'utilise pas de réflexion. Du moins tant que vous ne comptez pas le "casting" comme de la réflexion, de toute façon ;-).

0 votes

ToArray(T[]) le fait. Il doit créer un tableau du type approprié. Les JVM modernes optimisent ce type de réflexion pour être à peu près aussi rapide que la version non réfléchie.

0 votes

Je pense que cela utilise la réflexion. Le JDK 1.5.0_10 le fait sûrement et la réflexion est le seul moyen que je connaisse pour créer un tableau d'un type que vous ne connaissez pas au moment de la compilation.

2voto

Shimi Bandiel Points 3571

Le compilateur ne fera pas d'optimisation. Très probablement, il s'agira d'une optimisation du compilateur JIT à l'exécution et cela dépendra de votre JVM.

Personnellement, j'utilise l'ancienne version. Ce n'est pas aussi verbeux.

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