.toArray(nueva MiClase[0]) o .toArray(nueva MiClase[miList.size()])?
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...
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.025 ▒ 0.001 us/op
c.a.p.SO29378922.preSize 100 avgt 30 0.155 ▒ 0.004 us/op
c.a.p.SO29378922.preSize 1000 avgt 30 1.512 ▒ 0.031 us/op
c.a.p.SO29378922.preSize 5000 avgt 30 6.884 ▒ 0.130 us/op
c.a.p.SO29378922.preSize 10000 avgt 30 13.147 ▒ 0.199 us/op
c.a.p.SO29378922.preSize 100000 avgt 30 159.977 ▒ 5.292 us/op
c.a.p.SO29378922.resize 1 avgt 30 0.019 ▒ 0.000 us/op
c.a.p.SO29378922.resize 100 avgt 30 0.133 ▒ 0.003 us/op
c.a.p.SO29378922.resize 1000 avgt 30 1.075 ▒ 0.022 us/op
c.a.p.SO29378922.resize 5000 avgt 30 5.318 ▒ 0.121 us/op
c.a.p.SO29378922.resize 10000 avgt 30 10.652 ▒ 0.227 us/op
c.a.p.SO29378922.resize 100000 avgt 30 139.692 ▒ 8.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.
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).
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
size
y .toArray
Esto puede resultar ennull
s 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.