.toArray(nueva MiClase[0]) o .toArray(nueva MiClase[miList.size()])?

Resuelto itsadok asked hace 16 años • 8 respuestas

Suponiendo que tengo una ArrayList

ArrayList<MyClass> myList;

Y quiero llamar a toArray, ¿hay alguna razón de rendimiento para usar?

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

encima

MyClass[] arr = myList.toArray(new MyClass[0]);

?

Prefiero el segundo estilo, ya que es menos detallado y supuse que el compilador se asegurará de que la matriz vacía no se cree realmente, pero me he estado preguntando si eso es cierto.

Por supuesto, en el 99% de los casos no hay diferencia en un sentido u otro, pero me gustaría mantener un estilo consistente entre mi código normal y mis bucles internos optimizados...

itsadok avatar Oct 06 '08 19:10 itsadok
Aceptado

Contraintuitivamente, la versión más rápida, en Hotspot 8, es:

MyClass[] arr = myList.toArray(new MyClass[0]);

Ejecuté un micro benchmark usando jmh. Los resultados y el código se encuentran a continuación, lo que muestra que la versión con una matriz vacía supera consistentemente a la versión con una matriz predimensionada. Tenga en cuenta que si puede reutilizar una matriz existente del tamaño correcto, el resultado puede ser diferente.

Resultados de referencia (puntuación en microsegundos, menor = mejor):

Benchmark                      (n)  Mode  Samples    Score   Error  Units
c.a.p.SO29378922.preSize         1  avgt       30    0.0250.001  us/op
c.a.p.SO29378922.preSize       100  avgt       30    0.1550.004  us/op
c.a.p.SO29378922.preSize      1000  avgt       30    1.5120.031  us/op
c.a.p.SO29378922.preSize      5000  avgt       30    6.8840.130  us/op
c.a.p.SO29378922.preSize     10000  avgt       30   13.1470.199  us/op
c.a.p.SO29378922.preSize    100000  avgt       30  159.9775.292  us/op
c.a.p.SO29378922.resize          1  avgt       30    0.0190.000  us/op
c.a.p.SO29378922.resize        100  avgt       30    0.1330.003  us/op
c.a.p.SO29378922.resize       1000  avgt       30    1.0750.022  us/op
c.a.p.SO29378922.resize       5000  avgt       30    5.3180.121  us/op
c.a.p.SO29378922.resize      10000  avgt       30   10.6520.227  us/op
c.a.p.SO29378922.resize     100000  avgt       30  139.6928.957  us/op

Como referencia, el código:

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO29378922 {
  @Param({"1", "100", "1000", "5000", "10000", "100000"}) int n;
  private final List<Integer> list = new ArrayList<>();
  @Setup public void populateList() {
    for (int i = 0; i < n; i++) list.add(0);
  }
  @Benchmark public Integer[] preSize() {
    return list.toArray(new Integer[n]);
  }
  @Benchmark public Integer[] resize() {
    return list.toArray(new Integer[0]);
  }
}

Puede encontrar resultados similares, análisis completos y discusiones en la publicación del blog Arrays of Wisdom of the Ancients . Para resumir: el compilador JVM y JIT contiene varias optimizaciones que le permiten crear e inicializar de forma económica una nueva matriz del tamaño correcto, y esas optimizaciones no se pueden usar si usted mismo crea la matriz.

assylias avatar Apr 04 '2015 09:04 assylias

A partir de ArrayList en Java 5 , la matriz ya estará llena si tiene el tamaño correcto (o es más grande). Como consecuencia

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

creará un objeto de matriz, lo completará y lo devolverá a "arr". Por otro lado

MyClass[] arr = myList.toArray(new MyClass[0]);

creará dos matrices. El segundo es una matriz de MyClass con longitud 0. Entonces, hay una creación de objeto para un objeto que se desechará inmediatamente. Por lo que sugiere el código fuente, el compilador/JIT no puede optimizar este para que no se cree. Además, el uso del objeto de longitud cero da como resultado conversiones dentro del método toArray().

Vea la fuente de ArrayList.toArray():

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

Utilice el primer método para crear solo un objeto y evitar conversiones (implícitas pero costosas).

Georgi avatar Oct 06 '2008 12:10 Georgi

De la inspección JetBrains IntelliJ IDEA :

Hay dos estilos para convertir una colección en una matriz:

  • Una matriz predimensionada, por ejemplo,c.toArray(new String[c.size()])
  • Una matriz vacía, por ejemplo, c.toArray(new String[0])

En versiones anteriores de Java, se recomendaba utilizar una matriz de tamaño predeterminado, ya que la llamada de reflexión necesaria para crear una matriz del tamaño adecuado era bastante lenta.

Sin embargo, desde las últimas actualizaciones de OpenJDK 6, esta llamada se intrinsificó, lo que hizo que el rendimiento de la versión de matriz vacía fuera el mismo y, a veces, incluso mejor, en comparación con la versión de tamaño predeterminado. Además, pasar una matriz de tamaño predeterminado es peligroso para una colección concurrente o sincronizada, ya que es posible una carrera de datos entre las llamadas sizey . toArrayEsto puede resultar en nulls adicionales al final de la matriz si la colección se redujo simultáneamente durante la operación.

Utilice las opciones de inspección para seleccionar el estilo preferido.

Антон Антонов avatar Jun 07 '2018 09:06 Антон Антонов