Il modo più semplice per trasformare la raccolta in array?


125

Supponiamo di avere un Collection<Foo>. Qual è il modo migliore (il più breve in LoC nel contesto attuale) per trasformarlo Foo[]? Eventuali Ben noti librerie sono ammessi.

UPD: (un altro caso in questa sezione; lascia commenti se pensi che valga la pena creare un altro thread per esso): Che ne dici di trasformarti Collection<Foo>in Bar[]dove Barha il costruttore con 1 parametro di tipo, ad Fooesempio public Bar(Foo foo){ ... }?


Questa risposta è stata fornita con un'API alternativa introdotta in JDK-11 per eseguire la stessa operazione con prestazioni e spiegazioni simili. Inoltre la sintassi corrisponde Stream.toArrayall'API esistente dal JDK.
Naman,

Risposte:


256

Dov'è xla collezione:

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

35
più semplice (più facile) ma non migliore (memoria): x.toArray(new Foo[0]) --- documentazione : non c'è tempo per leggere ...
user85421

6
@Carlos in realtà, utilizza una memoria migliore di (new Foo[0]). Secondo la documentazione Collection.toArray, `@param a l'array in cui devono essere memorizzati gli elementi di questa raccolta, se è abbastanza grande`, il che significa che li memorizzerà direttamente in quel nuovo array. Se gli dai un array di dimensioni 0, creerà un nuovo array, il che significa che hai un array piccolo e un array grande che viene creato quando non è necessario.
corsiKa

4
@glowcoder - ma questo può essere minimizzato se si definisce un array vuoto una volta come costante statica. È solo un suggerimento per toArraycreare un array di destinazione del tipo corretto. E in questo caso, onestamente, non mi interesserebbe un singolo array vuoto aggiuntivo nella mia applicazione. È molto al di sotto del livello di rumore.
Andreas Dolk,

2
@glowcoder - questo è esattamente il motivo per cui (ho provato a ) scrivere che usare new Foo[0]è più semplice "ma non il migliore (memoria)" ... cioè, intendevo dire che la mia soluzione è più semplice ma non migliore (questo è il motivo che ho usato :).
user85421

21
Si noti che questo codice non è thread-safe, anche se si utilizza una raccolta sincronizzata. Si comporterà nella maggior parte dei casi, ma se un elemento viene eliminato tra la chiamata a x.size () e x.toArray (), l'array risultante conterrà un elemento null aggiuntivo alla fine. La new Foo[0]variante proposta da Carlos non presenta questo problema.
Jules

36

Soluzione alternativa alla domanda aggiornata utilizzando Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);

35
Questo può essere abbreviato in Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic il

Sintassi saggia Preferirei di gran lunga la risposta accettata. Vorrei che tutti potessero passare a Python.
Eric Chen,

@EricChen Passa a Python - un linguaggio interpretato e digitato in modo dinamico da Java - "linguaggio compilato e tipicamente statico" a causa della sintassi del codice e le linee numeriche differiscono? Non è una buona idea.
RafiAlhamd,

10

Se lo usi più di una volta o in un ciclo, potresti definire una costante

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

e fai la conversione come piace

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

Il toArraymetodo prenderà l'array vuoto per determinare il tipo corretto dell'array di destinazione e creare un nuovo array per te.


Ecco la mia proposta per l'aggiornamento:

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[]{});

1
ups, constnon è Java! public static final Foo[] FOO = {}
user85421

1
perché sarebbe pubblico?
Zoltán,

@Zoltán - perché no? È immutabile, quindi non presenta un problema di sicurezza. È un po 'di confusione, ma potrebbe essere utile in altri codici, quindi è generalmente innocuo. Forse persino creare una classe centrale con tutte queste costanti, e poi usarle import staticper raggiungerle.
Jules,

@Jules il riferimento alla matrice è immutabile, ma il suo contenuto non lo è. Chiunque al di fuori della classe può sostituire liberamente gli elementi dell'array. E d'altra parte, come regola generale, credo che tutti i campi dovrebbero essere resi privati ​​fino a quando non saranno necessari al di fuori della classe.
Zoltán,

2
Essendo di lunghezza zero, non ci sono contenuti che possono essere cambiati
Jules

5

Con JDK / 11, un modo alternativo di convertire un Collection<Foo>in un Foo[]potrebbe essere quello di utilizzare Collection.toArray(IntFunction<T[]> generator)come:

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

Come spiegato da @Stuart nella mailing list (sottolineatura mia), le prestazioni di questo dovrebbero essere essenzialmente le stesse di quelle esistenti Collection.toArray(new T[0])-

Il risultato è che le implementazioni che usano Arrays.copyOf() sono le più veloci, probabilmente perché sono intrinseche .

Può evitare di riempire zero l'array appena allocato perché sa che l'intero contenuto dell'array verrà sovrascritto. Questo è vero indipendentemente dall'aspetto dell'API pubblica.

L'implementazione dell'API all'interno di JDK recita:

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

L'implementazione predefinita chiama generator.apply(0)per ottenere un array di lunghezza zero e quindi chiama semplicemente toArray(T[]). Questo passa attraverso il Arrays.copyOf() percorso veloce, quindi è essenzialmente la stessa velocità di toArray(new T[0]).


Nota : - Solo che l'uso dell'API deve essere guidato insieme a un'incompatibilità all'indietro quando utilizzato per il codice connullvalori, ad esempiotoArray(null)poiché queste chiamate sarebbero ora ambigue a causa dell'esistentetoArray(T[] a)e non verrebbero compilate.



3

Per l'originale vedi la risposta doublep :

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

Per quanto riguarda l'aggiornamento:

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

3

Ecco la soluzione finale per il caso nella sezione aggiornamento (con l'aiuto di Google Collections):

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

Ma l'approccio chiave qui è stato menzionato nella risposta del doublep (ho dimenticato il toArraymetodo).


1
Vedi il mio commento sulla risposta di doublep sulla sicurezza del thread, che si applica anche a questa soluzione. new Bar[0]evita il problema.
Jules,

-1

Ad esempio, hai una raccolta ArrayList con elementi Classe studente:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.