¿La forma más fácil de transformar la colección en matriz?

125

Supongamos que tenemos a Collection<Foo>. ¿Cuál es la mejor manera (la más corta en LoC en el contexto actual) para transformarlo Foo[]? Cualquier biblioteca conocida está permitida.

UPD: (un caso más en esta sección; deje comentarios si cree que vale la pena crear otro hilo para él): ¿Qué pasa con la transformación Collection<Foo>a Bar[]donde Bartiene un constructor con 1 parámetro de tipo, Fooes decir public Bar(Foo foo){ ... }?

romano
fuente
Hizo esta respuesta con una API alternativa introducida en JDK-11 para realizar la misma operación con un rendimiento similar y la explicación junto con ella. Además, la sintaxis coincide con la Stream.toArrayAPI existente del JDK.
Naman

Respuestas:

256

¿Dónde xestá la colección?

Foo[] foos = x.toArray(new Foo[x.size()]);
doble
fuente
35
más simple (más fácil) pero no el mejor (memoria): x.toArray(new Foo[0]) --- documentación : no hay tiempo para leer ...
user85421
66
@Carlos en realidad, usa mejor memoria que (new Foo[0]). Según la documentación de Collection.toArray, `@param a la matriz en la que se almacenarán los elementos de esta colección, si es lo suficientemente grande`, lo que significa que los almacenará directamente en esa nueva matriz. Si le da una matriz de tamaño 0, creará una nueva matriz, lo que significa que tendrá una matriz pequeña y una matriz grande cuando no sea necesario.
corsiKa
44
@glowcoder, pero esto puede minimizarse si define una matriz vacía una vez como una constante estática. Es solo una pista para toArraycrear una matriz de destino del tipo correcto. Y en este caso, sinceramente, no me importaría un solo conjunto vacío adicional en mi aplicación. Eso está muy por debajo del nivel de ruido.
Andreas Dolk
2
@glowcoder: esa es exactamente la razón por la que ( intenté ) escribir que usar new Foo[0]es más simple "pero no la mejor (memoria)" ... es decir, mi solución es más fácil pero no la mejor (esa es la razón por la que lo usé :).
user85421
21
Tenga en cuenta que este código no es seguro para subprocesos, incluso si está utilizando una colección sincronizada. Se comportará en la mayoría de las situaciones, pero si se elimina un elemento entre la llamada a x.size () y x.toArray (), la matriz resultante contendrá un elemento nulo adicional al final. La new Foo[0]variante propuesta por Carlos no sufre este problema.
Julio
36

Solución alternativa a la pregunta actualizada utilizando Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);
Jules
fuente
35
Esto se puede acortar a Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic
En cuanto a la sintaxis, preferiría la respuesta aceptada. Deseo que todas las personas puedan cambiarse a Python.
Eric Chen
@EricChen Cambiar a Python, un lenguaje interpretado y tipado dinámicamente desde Java, ¿'lenguaje compilado y tipado estáticamente' debido a la sintaxis de codificación y las líneas numéricas difieren? No es Buena idea.
RafiAlhamd
10

Si lo usa más de una vez o en un bucle, podría definir una constante

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

y hacer la conversión como

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

El toArraymétodo tomará la matriz vacía para determinar el tipo correcto de la matriz de destino y creará una nueva matriz para usted.


Aquí está mi propuesta para la actualización:

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
fuente
1
ups, constno es Java! public static final Foo[] FOO = {}
user85421
1
¿Por qué sería público?
Zoltán
@ Zoltán: ¿por qué no? Es inmutable, por lo que no presenta un problema de seguridad. Es un poco desordenado, pero podría ser útil en otro código, por lo que generalmente es inofensivo. Tal vez incluso cree una clase central con todas estas constantes, y luego use import staticpara llegar a ellas.
Julio
@Jules la referencia a la matriz es inmutable, pero su contenido no lo es. Cualquier persona fuera de la clase puede reemplazar los elementos de la matriz libremente. Y, por otro lado, como regla general, creo que todos los campos deben hacerse privados hasta que se necesiten fuera de la clase.
Zoltán
2
Al ser de longitud cero, no hay contenido que pueda cambiarse
Julio
5

Con JDK / 11, una forma alternativa de convertir a Collection<Foo>a Foo[]podría ser utilizar 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

Según lo explicado por @Stuart en la lista de correo (el énfasis es mío), el rendimiento de este debe ser esencialmente el mismo que el de la ya existente Collection.toArray(new T[0])-

El resultado es que las implementaciones que usan Arrays.copyOf() son las más rápidas, probablemente porque es intrínseca .

Puede evitar llenar con cero la matriz recién asignada porque sabe que se sobrescribirá todo el contenido de la matriz. Esto es cierto independientemente de cómo se vea la API pública.

La implementación de la API dentro del JDK dice:

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

La implementación predeterminada llama generator.apply(0)para obtener una matriz de longitud cero y luego simplemente llama toArray(T[]). Esto pasa por el Arrays.copyOf() camino rápido, por lo que es esencialmente la misma velocidad que toArray(new T[0]).


Nota : - Solo que el uso de la API debe guiarse junto con una incompatibilidad hacia atrás cuando se usa para código connullvalores, por ejemplo,toArray(null)ya que estas llamadas ahora serían ambiguas debido a la existenciatoArray(T[] a)y no podrían compilarse.

Naman
fuente
4

Si usa Guava en su proyecto, puede usar Iterables::toArray.

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

Para el original, vea la respuesta doublep :

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

En cuanto a la actualización:

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

Aquí está la solución final para el caso en la sección de actualización (con la ayuda de Google Collections):

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

Pero, el enfoque clave aquí se mencionó en la respuesta de doublep (olvidé el toArraymétodo).

romano
fuente
1
Vea mi comentario sobre la respuesta de doublep sobre la seguridad del hilo, que también se aplica a esta solución. new Bar[0]evita el problema
Julio
-1

Por ejemplo, tiene una colección ArrayList con elementos Clase de estudiante:

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
fuente