A maneira mais fácil de transformar coleção em matriz?

125

Suponha que temos um Collection<Foo>. Qual é a melhor (mais curta no LoC no contexto atual) para transformá-lo Foo[]? Quaisquer bibliotecas conhecidas são permitidas.

UPD: (mais um caso nesta seção; deixe comentários se você acha que vale a pena criar outro thread para ele): Que tal transformar Collection<Foo>em Bar[]onde Bartem construtor com 1 parâmetro do tipo Fooie public Bar(Foo foo){ ... }?

romano
fonte
Fiz esta resposta com uma API alternativa introduzida no JDK-11 para executar a mesma operação com desempenho semelhante e a explicação junto. Além disso, a sintaxe corresponde à Stream.toArrayAPI existente do JDK.
Naman

Respostas:

256

Onde xestá a coleção:

Foo[] foos = x.toArray(new Foo[x.size()]);
doublep
fonte
35
mais simples (mais fácil), mas não melhor (memória): x.toArray(new Foo[0]) --- documentação : não há tempo para ler ...
user85421 20/07/10
6
@ Carlos, na verdade, ele usa melhor memória do que (new Foo[0]). De acordo com a documentação Collection.toArray, `@param a matriz na qual os elementos desta coleção devem ser armazenados, se forem grandes o suficiente`, o que significa que os armazenará diretamente nessa nova matriz. Se você fornecer uma matriz de tamanho 0, ela criará uma nova matriz, o que significa que você tem uma matriz pequena e uma matriz grande quando isso é desnecessário.
corsiKa
4
@glowcoder - mas isso pode ser minimizado se você definir uma matriz vazia uma vez como uma constante estática. É apenas uma dica para toArraycriar uma matriz de destino do tipo correto. E, nesse caso, sinceramente, eu não me importaria com uma única matriz vazia extra no meu aplicativo. Isso está muito abaixo do nível de ruído.
Andreas Dolk
2
@glowcoder - é exatamente por isso que eu ( tentei ) escrevi que usar new Foo[0]é mais simples "mas não é melhor (memória)" ... ou seja, eu quis dizer que minha solução é mais fácil, mas não é melhor (é por isso que eu usei :).
user85421
21
Observe que esse código não é seguro para threads, mesmo se você estiver usando uma coleção sincronizada. Ele se comportará na maioria das situações, mas se um item for excluído entre a chamada para x.size () e x.toArray (), a matriz resultante conterá um elemento nulo extra no final. A new Foo[0]variante proposta por Carlos não sofre esse problema.
Jul
36

Solução alternativa para a pergunta atualizada usando Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);
Jules
fonte
35
Isso pode ser encurtado para Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic
Em termos de sintaxe, prefiro a resposta aceita. Eu desejo que todas as pessoas possam mudar para Python.
Eric Chen
@EricChen Alterne para Python - uma linguagem interpretada e de tipo dinâmico do Java - 'linguagem compilada e de tipo estaticamente' devido à sintaxe de codificação e às linhas numéricas diferentes? Não é uma boa ideia.
RafiAlhamd 21/02
10

Se você o usar mais de uma vez ou em um loop, poderá definir uma constante

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

e faça a conversão como

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

O toArraymétodo utilizará a matriz vazia para determinar o tipo correto da matriz de destino e criará uma nova matriz para você.


Aqui está minha proposta para a atualização:

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
fonte
1
ups, constnão é Java! public static final Foo[] FOO = {}
user85421
1
por que isso seria público?
Zoltán
@ Zoltán - por que não? É imutável, portanto, não apresenta um problema de segurança. É um pouco confuso, mas pode ser útil em outro código, geralmente é inofensivo. Talvez até crie uma classe central com todas essas constantes e use-as import staticpara alcançá-las.
Jul
@Jules, a referência à matriz é imutável, mas seu conteúdo não é. Qualquer pessoa fora da classe pode substituir os elementos da matriz livremente. E, por outro lado, como regra geral, acredito que todos os campos devem ser privados até que sejam necessários fora da classe.
Zoltán
2
Sendo de tamanho zero, não há conteúdo que possa ser alterado.
Jul
5

Com o JDK / 11, uma maneira alternativa de converter um Collection<Foo>em um Foo[]poderia ser usar Collection.toArray(IntFunction<T[]> generator)como:

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

Conforme explicado por @Stuart na lista de discussão (grifo meu), o desempenho disso deve ser essencialmente o mesmo do existente Collection.toArray(new T[0])-

O resultado é que as implementações que usam Arrays.copyOf() são as mais rápidas, provavelmente por serem intrínsecas .

Ele pode evitar o preenchimento zero da matriz alocada recentemente, porque sabe que todo o conteúdo da matriz será substituído. Isso ocorre independentemente da aparência da API pública.

A implementação da API no JDK diz:

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

A implementação padrão chama generator.apply(0)para obter uma matriz de tamanho zero e simplesmente chama toArray(T[]). Isso passa pelo Arrays.copyOf() caminho rápido, portanto é essencialmente a mesma velocidade que toArray(new T[0]).


Nota : - Apenas o uso da API deve ser orientado juntamente com uma incompatibilidade reversa quando usado para código comnullvalores, por exemplo,toArray(null)uma vez que essas chamadas agora seriam ambíguas por causa da existênciatoArray(T[] a)e falhariam na compilação.

Naman
fonte
4

Se você usa o Guava no seu projeto, pode usá-lo Iterables::toArray.

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

Para o original, veja a resposta doublep :

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

Quanto à atualização:

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

Aqui está a solução final para o caso na seção de atualização (com a ajuda das coleções do Google):

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

Mas, a principal abordagem aqui foi mencionada na resposta do doublep (eu esqueci o toArraymétodo).

romano
fonte
1
Veja meu comentário sobre a resposta da doublep sobre segurança de threads, que também se aplica a esta solução. new Bar[0]evita o problema.
Jul
-1

Por exemplo, você tem a coleção ArrayList com os elementos 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
fonte