Le moyen le plus simple de transformer une collection en tableau?

125

Supposons que nous ayons un Collection<Foo>. Quelle est la meilleure façon (la plus courte en LoC dans le contexte actuel) de la transformer Foo[]? Toutes les bibliothèques connues sont autorisées.

UPD: (un cas de plus dans cette section; laissez des commentaires si vous pensez que cela vaut la peine de créer un autre thread pour cela): Qu'en est-il de la transformation Collection<Foo>en Bar[]where Barhas constructeur avec 1 paramètre de type Fooie public Bar(Foo foo){ ... }?

romain
la source
Fait cette réponse avec une API alternative introduite dans JDK-11 pour effectuer la même opération avec des performances similaires et l'explication avec elle. De plus, la syntaxe correspond à l' Stream.toArrayAPI existante du JDK.
Naman

Réponses:

256

xest la collection:

Foo[] foos = x.toArray(new Foo[x.size()]);
double
la source
35
plus simple (plus facile) mais pas meilleur (mémoire): x.toArray(new Foo[0]) --- documentation : pas le temps de lire ...
user85421
6
@Carlos en fait, il utilise une meilleure mémoire que (new Foo[0]). Selon la documentation Collection.toArray, `@param est le tableau dans lequel les éléments de cette collection doivent être stockés, s'il est suffisamment grand`, ce qui signifie qu'il les stockera directement dans ce nouveau tableau. Si vous lui donnez un tableau de taille 0, il créera un nouveau tableau, ce qui signifie que vous avez un petit tableau et un grand tableau en cours de création lorsque cela n'est pas nécessaire.
corsiKa
4
@glowcoder - mais cela peut être minimisé si vous définissez une fois un tableau vide comme constante statique. C'est juste un indice pour toArraycréer un tableau cible du type correct. Et dans ce cas, honnêtement, je ne me soucierais pas d'un seul tableau vide supplémentaire dans mon application. C'est bien en dessous du niveau de bruit.
Andreas Dolk
2
@glowcoder - c'est exactement pourquoi j'ai ( essayé ) d'écrire que l'utilisation new Foo[0]est plus simple "mais pas la meilleure (mémoire)" ... c'est-à-dire que ma solution est plus simple mais pas la meilleure (c'est la raison pour laquelle j'ai utilisé :).
user85421
21
Notez que ce code n'est pas threadsafe, même si vous utilisez une collection synchronisée. Il se comportera lui-même dans la plupart des situations, mais si un élément est supprimé entre l'appel à x.size () et x.toArray (), le tableau résultant contiendra un élément nul supplémentaire à la fin. La new Foo[0]variante proposée par Carlos ne souffre pas de ce problème.
Jules
36

Solution alternative à la question mise à jour en utilisant Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);
Jules
la source
35
Cela peut être raccourci à Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic
En termes de syntaxe, je préférerais de loin la réponse acceptée. Je souhaite que les gens puissent tous passer à Python.
Eric Chen
@EricChen Passer à Python - un langage interprété et typé dynamiquement de Java - 'langage compilé et typé statiquement' en raison de la syntaxe de codage et des lignes numériques diffèrent? Pas une bonne idée.
RafiAlhamd le
10

Si vous l'utilisez plus d'une fois ou en boucle, vous pouvez définir une constante

public static final Foo[] FOO = new Foo[]{};

et faire la conversion comme

Foo[] foos = fooCollection.toArray(FOO);

La toArrayméthode utilise le tableau vide pour déterminer le type correct du tableau cible et crée un nouveau tableau pour vous.


Voici ma proposition de mise à jour:

Collection<Foo> foos = new ArrayList<Foo>();
Collection<Bar> temp = new ArrayList<Bar>();
for (Foo foo:foos) 
    temp.add(new Bar(foo));
Bar[] bars = temp.toArray(new Bar[]{});
Andreas Dolk
la source
1
ups, ce constn'est pas Java! public static final Foo[] FOO = {}
user85421
1
pourquoi serait-il public?
Zoltán
@ Zoltán - pourquoi pas? C'est immuable, donc ne présente pas de problème de sécurité. C'est un peu encombrant, mais pourrait être utile dans un autre code, donc généralement inoffensif. Peut-être même créer une classe centrale avec toutes ces constantes, puis l'utiliser import staticpour les atteindre.
Jules
@Jules la référence au tableau est immuable, mais son contenu ne l'est pas. Toute personne en dehors de la classe peut remplacer librement les éléments du tableau. Et d'un autre côté, en règle générale, je crois que tous les champs devraient être rendus privés jusqu'à ce qu'ils soient nécessaires en dehors de la classe.
Zoltán
2
Étant de longueur nulle, aucun contenu ne peut être modifié
Jules
5

Avec JDK / 11, une autre façon de convertir un Collection<Foo>en un Foo[]pourrait être d'utiliser Collection.toArray(IntFunction<T[]> generator)comme:

Foo[] foos = fooCollection.toArray(new Foo[0]); // before JDK 11
Foo[] updatedFoos = fooCollection.toArray(Foo[]::new); // after JDK 11

Comme expliqué par @Stuart sur la liste de diffusion (c'est moi qui souligne), les performances de celui-ci devraient essentiellement être les mêmes que celles de l'existant Collection.toArray(new T[0])-

Le résultat est que les implémentations qui utilisent Arrays.copyOf() sont les plus rapides, probablement parce que c'est intrinsèque .

Il peut éviter de remplir à zéro le tableau fraîchement alloué car il sait que tout le contenu du tableau sera écrasé. Cela est vrai quel que soit l'aspect de l'API publique.

La mise en œuvre de l'API dans le JDK se lit comme suit:

default <T> T[] toArray(IntFunction<T[]> generator) {
    return toArray(generator.apply(0));
}

L'implémentation par défaut appelle generator.apply(0)pour obtenir un tableau de longueur nulle, puis appelle simplement toArray(T[]). Cela passe par le Arrays.copyOf() chemin rapide, donc c'est essentiellement la même vitesse que toArray(new T[0]).


Remarque : - Simplement que l'utilisation de l'API doit être guidée avec une incompatibilité descendante lorsqu'elle est utilisée pour du code avec desnullvaleurs, par exempletoArray(null)puisque ces appels seraient maintenant ambigus en raison de l'existanttoArray(T[] a)et échoueraient à se compiler.

Naman
la source
4

Si vous utilisez Guava dans votre projet, vous pouvez utiliser Iterables::toArray.

Foo[] foos = Iterables.toArray(x, Foo.class);
Andrea Bergonzo
la source
3

Pour l'original, voir la réponse doublep :

Foo[] a = x.toArray(new Foo[x.size()]);

Quant à la mise à jour:

int i = 0;
Bar[] bars = new Bar[fooCollection.size()];
for( Foo foo : fooCollection ) { // where fooCollection is Collection<Foo>
    bars[i++] = new Bar(foo);
}    
OscarRyz
la source
3

Voici la solution finale pour le cas dans la section mise à jour (avec l'aide de Google Collections):

Collections2.transform (fooCollection, new Function<Foo, Bar>() {
    public Bar apply (Foo foo) {
        return new Bar (foo);
    }
}).toArray (new Bar[fooCollection.size()]);

Mais, l'approche clé ici a été mentionnée dans la réponse du doublep (j'ai oublié la toArrayméthode).

romain
la source
1
Voir mon commentaire sur la réponse de doublep sur la sécurité des threads, qui s'applique également à cette solution. new Bar[0]évite le problème.
Jules
-1

Par exemple, vous avez la collection ArrayList avec des éléments Student class:

List stuList = new ArrayList();
Student s1 = new Student("Raju");
Student s2 = new Student("Harish");
stuList.add(s1);
stuList.add(s2);
//now you can convert this collection stuList to Array like this
Object[] stuArr = stuList.toArray();           // <----- toArray() function will convert collection to array
Jagadeesh HN
la source