Der einfachste Weg, eine Sammlung in ein Array umzuwandeln?

125

Angenommen, wir haben eine Collection<Foo>. Was ist der beste (im aktuellen Kontext kürzeste in LoC) Weg, um es zu transformieren Foo[]? Alle bekannten Bibliotheken sind erlaubt.

UPD: (ein weiterer Fall in diesem Abschnitt, Kommentare zu hinterlassen , wenn Sie denken , es lohnt sich einen weiteren Thread für sie zu schaffen): Was ist die Umwandlung Collection<Foo>zu Bar[]dem Barhat Konstruktor mit 1 Parameter vom Typ Fooalso public Bar(Foo foo){ ... }?

römisch
quelle
Diese Antwort wurde mit einer in JDK-11 eingeführten alternativen API gegeben, um denselben Vorgang mit ähnlicher Leistung und der dazugehörigen Erklärung auszuführen. Außerdem stimmt die Syntax mit der vorhandenen Stream.toArrayAPI aus dem JDK überein .
Naman

Antworten:

256

Wo xist die Sammlung:

Foo[] foos = x.toArray(new Foo[x.size()]);
doublep
quelle
35
einfacher (einfacher) aber nicht am besten (Speicher): x.toArray(new Foo[0]) --- Dokumentation : keine Zeit zum Lesen ...
user85421
6
@ Carlos tatsächlich verwendet es einen besseren Speicher als (new Foo[0]). Gemäß der Collection.toArray-Dokumentation ist "@param a das Array, in dem die Elemente dieser Sammlung gespeichert werden sollen, wenn sie groß genug sind", was bedeutet, dass sie direkt in diesem neuen Array gespeichert werden. Wenn Sie ihm ein Array der Größe 0 geben, wird ein neues Array erstellt. Dies bedeutet, dass ein kleines Array und ein großes Array erstellt werden, wenn dies nicht erforderlich ist.
CorsiKa
4
@glowcoder - Dies kann jedoch minimiert werden, wenn Sie ein leeres Array einmal als statische Konstante definieren. Dies ist nur ein Hinweis toArray, um ein Zielarray des richtigen Typs zu erstellen. Und in diesem Fall würde mich ehrlich gesagt kein zusätzliches leeres Array in meiner Anwendung interessieren. Das liegt weit unter dem Geräuschpegel.
Andreas Dolk
2
@glowcoder - genau deshalb habe ich ( versucht ) geschrieben, dass die Verwendung new Foo[0]einfacher ist "aber nicht am besten (Speicher)" ... das heißt, ich meinte, dass meine Lösung einfacher, aber nicht am besten ist (das ist der Grund, warum ich sie verwendet habe :).
user85421
21
Beachten Sie, dass dieser Code nicht threadsicher ist, selbst wenn Sie eine synchronisierte Sammlung verwenden. Es verhält sich in den meisten Situationen von selbst. Wenn jedoch ein Element zwischen dem Aufruf von x.size () und x.toArray () gelöscht wird, enthält das resultierende Array am Ende ein zusätzliches Nullelement. Die new Foo[0]von Carlos vorgeschlagene Variante leidet nicht unter diesem Problem.
Jules
36

Alternative Lösung für die aktualisierte Frage mit Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);
Jules
quelle
35
Dies kann aufBar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic
In Bezug auf die Syntax würde ich die akzeptierte Antwort sehr bevorzugen. Ich wünschte, die Leute könnten alle zu Python wechseln.
Eric Chen
@EricChen Zu Python wechseln - eine interpretierte und dynamisch typisierte Sprache aus Java - 'kompilierte und statisch typisierte Sprache' aufgrund unterschiedlicher Codierungssyntax und Zahlenlinien? Keine gute Idee.
RafiAlhamd
10

Wenn Sie es mehrmals oder in einer Schleife verwenden, können Sie eine Konstante definieren

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

und mache die Konvertierung wie es ist

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

Die toArrayMethode verwendet das leere Array, um den richtigen Typ des Zielarrays zu bestimmen und ein neues Array für Sie zu erstellen.


Hier ist mein Vorschlag für das Update:

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
quelle
1
ups, constist nicht Java! public static final Foo[] FOO = {}
user85421
1
warum sollte es öffentlich sein?
Zoltán
@ Zoltán - warum nicht? Es ist unveränderlich und stellt daher kein Sicherheitsproblem dar. Es ist ein bisschen unübersichtlich, könnte aber in anderem Code nützlich sein, ist also im Allgemeinen harmlos. Vielleicht erstellen Sie sogar eine zentrale Klasse mit all diesen Konstanten und verwenden sie dann import static, um an sie heranzukommen.
Jules
@Jules Der Verweis auf das Array ist unveränderlich, der Inhalt jedoch nicht. Jeder außerhalb der Klasse kann die Elemente des Arrays frei ersetzen. Andererseits glaube ich als Faustregel, dass alle Felder privat gemacht werden sollten, bis sie außerhalb der Klasse benötigt werden.
Zoltán
2
Da die Länge Null, es gibt keine Inhalte , die geändert werden können
Jules
5

Bei JDK / 11 besteht eine alternative Möglichkeit zum Konvertieren von a Collection<Foo>in a Foo[]darin, Folgendes zu verwenden Collection.toArray(IntFunction<T[]> generator):

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

Wie von @Stuart auf der Mailingliste (Schwerpunkt Mine) erläutert , sollte die Leistung im Wesentlichen die gleiche sein wie die der vorhandenen Collection.toArray(new T[0])-

Das Ergebnis ist, dass Implementierungen, die Arrays.copyOf() verwenden, am schnellsten sind, wahrscheinlich weil es eine intrinsische ist .

Es kann vermieden werden, dass das frisch zugewiesene Array mit Null gefüllt wird, da es weiß, dass der gesamte Array-Inhalt überschrieben wird. Dies gilt unabhängig davon, wie die öffentliche API aussieht.

Die Implementierung der API im JDK lautet:

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

Die Standardimplementierung ruft generator.apply(0)auf, um ein Array mit der Länge Null abzurufen, und ruft dann einfach auf toArray(T[]). Dies geht durch den Arrays.copyOf() schnellen Weg, also ist es im Wesentlichen die gleiche Geschwindigkeit wie toArray(new T[0]).


Hinweis : - Nur, dass die API-Verwendung mit einer Abwärtsinkompatibilität einhergeht, wenn sie für Code mitnullWerten verwendet wird, z. B.toArray(null)da diese Aufrufe jetzt aufgrund vorhandener Aufrufe mehrdeutig sindtoArray(T[] a)und nicht kompiliert werden können.

Naman
quelle
4

Wenn Sie Guava in Ihrem Projekt verwenden, können Sie verwenden Iterables::toArray.

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

Für das Original siehe doppelte Antwort:

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

Wie für das Update:

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

Hier ist die endgültige Lösung für den Fall im Update-Bereich (mithilfe von Google Collections):

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

Aber der Schlüsselansatz hier wurde in der Antwort des Doubleps erwähnt (ich habe die toArrayMethode vergessen ).

römisch
quelle
1
Siehe meinen Kommentar zur Antwort von doublep zur Thread-Sicherheit, die auch für diese Lösung gilt. new Bar[0]vermeidet das Problem.
Jules
-1

Beispiel: Sie haben eine Sammlung ArrayList mit Elementen. Schülerklasse:

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
quelle