Najłatwiejszy sposób przekształcenia kolekcji w tablicę?

125

Załóżmy, że mamy plik Collection<Foo>. Jaki jest najlepszy (najkrótszy w LoC w obecnym kontekście) sposób, aby go przekształcić Foo[]? Wszelkie dobrze znane biblioteki są dozwolone.

UPD: (jeszcze jeden przypadek w tej sekcji; zostaw komentarz, jeśli uważasz, że warto utworzyć dla niego kolejny wątek): A co z transformacją Collection<Foo>do Bar[]gdzie Barma konstruktor z 1 parametrem typu Footj. public Bar(Foo foo){ ... }?

rzymski
źródło
Uczyniono tę odpowiedź za pomocą alternatywnego interfejsu API wprowadzonego w JDK-11, aby wykonać tę samą operację z podobną wydajnością i wraz z wyjaśnieniem. Dodatkowo składnia jest zgodna z istniejącym Stream.toArrayinterfejsem API z JDK.
Naman

Odpowiedzi:

256

Gdzie xjest kolekcja:

Foo[] foos = x.toArray(new Foo[x.size()]);
doublep
źródło
35
prostsze (łatwiejsze), ale nie najlepsze (pamięć): x.toArray(new Foo[0]) --- dokumentacja : nie ma czasu na czytanie ...
user85421
6
@Carlos w rzeczywistości wykorzystuje lepszą pamięć niż (new Foo[0]). Zgodnie z dokumentacją Collection.toArray, `@param a tablica, w której mają być przechowywane elementy tej kolekcji, jeśli jest wystarczająco duża`, co oznacza, że ​​będzie przechowywać je bezpośrednio w tej nowej tablicy. Jeśli nadasz mu tablicę o rozmiarze 0, utworzy nową tablicę, co oznacza, że ​​masz małą tablicę i dużą tablicę, która jest tworzona, gdy taka jest niepotrzebna.
corsiKa
4
@glowcoder - ale można to zminimalizować, jeśli raz zdefiniujesz pustą tablicę jako stałą statyczną. To tylko wskazówka dotycząca toArraytworzenia docelowej tablicy odpowiedniego typu. I w tym przypadku, szczerze, nie obchodziłabym jedna dodatkowa pusta tablica w mojej aplikacji. To znacznie poniżej poziomu hałasu.
Andreas Dolk
2
@glowcoder - właśnie dlatego ( starałem się ) napisać, że używanie new Foo[0]jest prostsze "ale nie najlepsze (pamięć)" ... czyli chodziło mi o to, że moje rozwiązanie jest łatwiejsze, ale nie najlepsze (z tego powodu użyłem :).
user85421
21
Zwróć uwagę, że ten kod nie jest bezpieczny wątkowo, nawet jeśli używasz zsynchronizowanej kolekcji. W większości sytuacji zachowuje się sam, ale jeśli element zostanie usunięty między wywołaniem x.size () a x.toArray (), wynikowa tablica będzie zawierała dodatkowy element zerowy na końcu. new Foo[0]Wariant zaproponowany przez Carlos nie cierpi tego problemu.
Jules
36

Alternatywne rozwiązanie zaktualizowanego pytania za pomocą Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);
Jules
źródło
35
Można to skrócić do Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic
Mądra składnia wolałbym zaakceptowaną odpowiedź. Chciałbym, żeby wszyscy mogli przejść na Pythona.
Eric Chen
@EricChen Przełącz na Python - interpretowany i dynamicznie wpisywany język z Javy - „język kompilowany i typowany statycznie” ze względu na różnice w składni kodu i liniach liczbowych? Nie jest to dobry pomysł.
RafiAlhamd
10

Jeśli używasz go więcej niż raz lub w pętli, możesz zdefiniować stałą

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

i zrób konwersję tak, jak chcesz

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

Plik toArrayMetoda zajmie pustą tablicę, aby ustalić właściwy rodzaj tablicy docelowej i stworzyć nową tablicę dla Ciebie.


Oto moja propozycja aktualizacji:

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
źródło
1
ups, constto nie Java! public static final Foo[] FOO = {}
user85421
1
dlaczego miałoby być publiczne?
Zoltán
@ Zoltán - czemu nie? Jest niezmienny, więc nie stanowi problemu z bezpieczeństwem. Jest trochę bałaganu, ale może być przydatny w innym kodzie, więc jest ogólnie nieszkodliwy. Może nawet utwórz klasę centralną ze wszystkimi tymi stałymi, a następnie użyj, import staticaby się do nich dostać.
Jules
@Jules odwołanie do tablicy jest niezmienne, ale jego zawartość nie. Każdy spoza klasy może dowolnie zastępować elementy tablicy. Z drugiej strony, z reguły uważam, że wszystkie pola powinny być prywatne, dopóki nie będą potrzebne poza klasą.
Zoltán
2
Ponieważ długość jest zerowa, nie ma treści, które można zmienić
Jules
5

W przypadku JDK / 11 alternatywnym sposobem konwersji a Collection<Foo>na an Foo[]może być wykorzystanie Collection.toArray(IntFunction<T[]> generator)jako:

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

Jak wyjaśnił @Stuart na liście mailingowej ( wyróżnienie moje), wydajność powinna być zasadniczo taka sama, jak w przypadku istniejącej Collection.toArray(new T[0])-

W rezultacie implementacje używające Arrays.copyOf() są najszybsze, prawdopodobnie dlatego, że jest to nieodłączne .

Może uniknąć wypełniania świeżo przydzielonej tablicy zerami, ponieważ wie, że cała zawartość tablicy zostanie nadpisana. Dzieje się tak niezależnie od tego, jak wygląda publiczny interfejs API.

Implementacja API w JDK czyta:

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

Domyślna implementacja wywołuje generator.apply(0)tablicę o zerowej długości, a następnie po prostu wywołuje toArray(T[]). Przechodzi przez Arrays.copyOf() szybką ścieżkę, więc jest zasadniczo taka sama jak prędkość toArray(new T[0]).


Uwaga : - Tylko, że użycie API powinno być prowadzone wraz z wsteczną niezgodnością, gdy jest używany do kodu znullwartościami, np.toArray(null)Ponieważ te wywołania byłyby teraz niejednoznaczne z powodu istnieniatoArray(T[] a)i nie mogłyby się skompilować.

Naman
źródło
4

Jeśli używasz guawy w swoim projekcie, możesz użyć Iterables::toArray.

Foo[] foos = Iterables.toArray(x, Foo.class);
Andrea Bergonzo
źródło
3

Oryginał znajduje się w podwójnej odpowiedzi:

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

Co do aktualizacji:

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

Oto ostateczne rozwiązanie sprawy w sekcji aktualizacji (z pomocą Google Collections):

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

Ale kluczowe podejście tutaj zostało wspomniane w odpowiedzi doublep (zapomniałem o toArraymetodzie).

rzymski
źródło
1
Zobacz mój komentarz do odpowiedzi firmy Doublep na temat bezpieczeństwa nici, która dotyczy również tego rozwiązania. new Bar[0]unika problemu.
Jules
-1

Na przykład masz kolekcję ArrayList z elementami 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
źródło