Найпростіший спосіб перетворення колекції в масив?


125

Припустимо, у нас є Collection<Foo>. Який найкращий (найкоротший у LoC у поточному контексті) спосіб трансформувати його Foo[]? Дозволені будь -які відомі бібліотеки.

UPD: (ще один випадок у цьому розділі; залиште коментарі, якщо ви вважаєте, що варто створити для нього ще одну нитку): Що з перетворенням Collection<Foo>на те, Bar[]де Barмає конструктор з 1 параметром типу, Fooтобто public Bar(Foo foo){ ... }?


Зробив цю відповідь за допомогою альтернативного API, введеного в JDK-11, для виконання тієї ж операції з аналогічною продуктивністю та поясненням разом з нею. Плюс синтаксис відповідає наявному Stream.toArrayAPI від JDK.
Наман

Відповіді:


256

Де xколекція:

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

35
простіше (простіше), але не найкраще (пам'ять): x.toArray(new Foo[0]) --- документація : немає часу для читання ...
user85421

6
@Carlos насправді він використовує кращу пам'ять, ніж (new Foo[0]). Відповідно до документації Collection.toArray, `@param - це масив, в який потрібно зберігати елементи цієї колекції, якщо він досить великий ', що означає, що він буде зберігати їх безпосередньо у новому масиві. Якщо ви дасте йому масив розміром 0, він створить новий масив, це означає, що у вас є малий масив і великий масив, коли такий робиться непотрібним.
corsiKa

4
@glowcoder - але це можна звести до мінімуму, якщо визначити порожній масив один раз як статичну константу. Це просто натяк toArrayна створення цільового масиву правильного типу. І в цьому випадку, чесно кажучи, я б не переймався одним зайвим порожнім масивом у своїй програмі. Це набагато нижче рівня шуму.
Андреас Долк

2
@glowcoder - саме тому я ( спробував ) написав, що використовувати new Foo[0]простіше ", але не найкраще (пам'ять)" ... тобто, я мав на увазі, що моє рішення простіше, але не найкраще (саме тому я і використовував :).
користувач85421

21
Зауважте, що цей код не є безпечним для потоків, навіть якщо ви використовуєте синхронізовану колекцію. Він поводитиме себе в більшості ситуацій, але якщо елемент буде видалено між викликом до x.size () та x.toArray (), отриманий масив буде містити додатковий нульовий елемент в кінці. new Foo[0]Варіант , запропонований Карлос не страждає від цієї проблеми.
Жуль

36

Альтернативне рішення оновленого питання за допомогою Java 8:

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

35
Це можна скоротити до Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic

Синтаксис мудрий, я б більше віддав перевагу прийнятій відповіді. Я бажаю, щоб люди могли перейти на Python.
Ерік Чен

@EricChen Перейти на Python - інтерпретована та динамічно набрана мова від Java - "компільована та статично набрана мова" через синтаксис кодування та рядки чисел різняться? Не гарна ідея.
RafiAlhamd

10

Якщо ви використовуєте його не один раз або в циклі, ви можете визначити константу

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

і зробити перетворення, як це

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

toArrayМетод буде приймати порожній масив , щоб визначити правильний тип цільового масиву і створити новий масив для вас.


Ось моя пропозиція щодо оновлення:

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, constце не Java! public static final Foo[] FOO = {}
користувач85421

1
чому це було б публічно?
Золтан

@ Zoltán - чому б і ні? Це незмінне, тому не представляє проблеми безпеки. Це трохи неспокійно, але може бути корисним і в іншому коді, тому загалом нешкідливий. Можливо, навіть створити центральний клас з усіма цими константами, а потім скористатися, import staticщоб дістатися до них.
Жуль

@Jules посилання на масив незмінне, але його вміст не є. Будь-хто поза класом може вільно замінити елементи масиву. А з іншого боку, як правило, я вважаю, що всі поля повинні бути приватними, поки вони не знадобляться поза класом.
Золтан

2
Будучи нульовою довжиною, немає вмісту, який можна змінити
Jules

5

З JDK / 11, альтернативним способом перетворення Collection<Foo>в Foo[]може використовувати Collection.toArray(IntFunction<T[]> generator)як:

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

Як пояснив @Stuart у списку розсилки (моє наголос), ефективність цього по суті повинна бути такою ж, як у існуючих Collection.toArray(new T[0])-

Підсумок полягає в тому, що реалізації, які використовують Arrays.copyOf(), є найшвидшими, ймовірно, тому, що вони є сутнісними .

Він може уникнути заповнення нуля свіжо виділеним масивом, оскільки знає, що весь вміст масиву буде перезаписаний. Це справедливо незалежно від того, як виглядає публічний API.

Реалізація API в JDK говорить:

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

Реалізація за замовчуванням викликає generator.apply(0)отримання масиву нульової довжини, а потім просто дзвінки toArray(T[]). Це проходить через Arrays.copyOf() швидкий шлях, тому це по суті така ж швидкість, як і toArray(new T[0]).


Примітка : - Просто використання API повинно керуватися разом із зворотною несумісністю при використанні для коду зіnullзначеннями, наприклад,toArray(null)оскільки ці виклики тепер будуть неоднозначними через існуючіtoArray(T[] a)та не зможуть компілюватись.



3

Для оригіналу див. Подвійну відповідь:

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

Щодо оновлення:

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

3

Ось остаточне рішення справи в розділі оновлення (за допомогою Google Collections):

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

Але ключовий підхід тут згадувався у відповіді подвійного (я забув про toArrayметод).


1
Дивіться мій коментар до відповіді дублера щодо безпеки потоку, що також стосується цього рішення. new Bar[0]уникає проблеми.
Жуль

-1

Наприклад, у вас є колекція ArrayList з елементами класу Student:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.