Самый простой способ преобразовать коллекцию в массив?

125

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

UPD: (еще один случай в этом разделе; оставьте комментарии, если считаете, что для него стоит создать еще один поток): Как насчет преобразования Collection<Foo>в Bar[]where Barhas конструктор с 1 параметром типа, Fooт.е. public Bar(Foo foo){ ... }?

Римский
источник
Сделал этот ответ с помощью альтернативного API, представленного в JDK-11, для выполнения той же операции с аналогичной производительностью и объяснениями вместе с ней. Кроме того, синтаксис соответствует существующему Stream.toArrayAPI от JDK.
Наман

Ответы:

256

Где xколлекция:

Foo[] foos = x.toArray(new Foo[x.size()]);
doublep
источник
35
проще (проще), но не лучше (память): x.toArray(new Foo[0]) --- документация : некогда читать ...
user85421
6
@Carlos на самом деле использует лучшую память, чем (new Foo[0]). Согласно документации Collection.toArray, `@param a массив, в котором должны храниться элементы этой коллекции, если он достаточно большой`, что означает, что он будет хранить их непосредственно в этом новом массиве. Если вы дадите ему массив размера 0, он создаст новый массив, что означает, что у вас есть небольшой массив и большой массив, который создается, когда в этом нет необходимости.
corsiKa
4
@glowcoder - но это можно свести к минимуму, если вы определите пустой массив один раз как статическую константу. Это просто подсказка для toArrayсоздания целевого массива правильного типа. И в этом случае, честно говоря, мне было бы наплевать на один лишний пустой массив в моем приложении. Это намного ниже уровня шума.
Андреас Долк
2
@glowcoder - именно поэтому я ( пытался ) написал, что использование new Foo[0]проще «но не лучшее (память)» ... то есть я имел в виду, что мое решение проще, но не лучшее (вот почему я использовал :).
user85421
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
упы, constэто не ява! public static final Foo[] FOO = {}
user85421
1
почему это должно быть публично?
Zoltán
@ Zoltán - почему бы и нет? Он неизменен, поэтому не представляет угрозы для безопасности. Это немного беспорядочно, но может быть полезно в другом коде, поэтому обычно безвредно. Может быть, даже создать центральный класс со всеми этими константами, а затем использовать import staticдля их получения.
Жюль
@Jules ссылка на массив неизменна, но его содержимое - нет. Любой человек вне класса может свободно заменять элементы массива. С другой стороны, я считаю, что все поля должны быть закрытыми до тех пор, пока они не понадобятся вне класса.
Золтан
2
Поскольку длина равна нулю, нет содержимого, которое можно было бы изменить
Джулс
5

В JDK / 11 альтернативный способ преобразования a Collection<Foo>в an Foo[]может заключаться в использовании Collection.toArray(IntFunction<T[]> generator)as:

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)и не смогут компилироваться.

Naman
источник
4

Если вы используете Guava в своем проекте, вы можете использовать Iterables::toArray.

Foo[] foos = Iterables.toArray(x, Foo.class);
Андреа Бергонцо
источник
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);
}    
OscarRyz
источник
3

Вот окончательное решение для случая в разделе обновлений (с помощью Google Collections):

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

Но ключевой подход здесь был упомянут в ответе doublep (я забыл о toArrayметоде).

Римский
источник
1
См. Мой комментарий к ответу doublep о безопасности потоков, который также относится к этому решению. new Bar[0]позволяет избежать проблемы.
Жюль
-1

Например, у вас есть коллекция ArrayList с элементами 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
Джагадиш Х.Н.
источник