Suponiendo que tengo una ArrayList
ArrayList<MyClass> myList;
Y quiero llamar a Array, ¿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 asumí 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 hace la diferencia de una forma u otra, pero me gustaría mantener un estilo consistente entre mi código normal y mis bucles internos optimizados ...
java
performance
coding-style
itsadok
fuente
fuente
Respuestas:
Contraintuitivamente, la versión más rápida, en Hotspot 8, es:
He ejecutado un micro benchmark usando jmh, los resultados y el código están a continuación, lo que muestra que la versión con una matriz vacía supera constantemente la versión con una matriz preestablecida. 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):
Como referencia, el código:
Puede encontrar resultados similares, análisis completo y discusión en la publicación de 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 de tamaño correcto, y esas optimizaciones no se pueden usar si crea la matriz usted mismo.
fuente
MyClass[] arr = myList.stream().toArray(MyClass[]::new);
... que supongo que serían más lentos. Además, me gustaría ver puntos de referencia para la diferencia con la declaración de matriz. Como en la diferencia entre:MyClass[] arr = new MyClass[myList.size()]; arr = myList.toArray(arr);
yMyClass[] arr = myList.toArray(new MyClass[myList.size()]);
... ¿o no debería haber ninguna diferencia? Supongo que estos dos son un problema que está fuera de lastoArray
funciones que suceden. ¡Pero hey! No pensé que aprendería sobre las otras intrincadas diferencias.toArray
directas (cuanto menor es el tamaño, mayor es la sobrecarga relativa)A partir de ArrayList en Java 5 , la matriz ya se completará si tiene el tamaño correcto (o es más grande). Por consiguiente
creará un objeto de matriz, lo llenará y lo devolverá a "arr". Por otra parte
creará dos matrices. El segundo es una matriz de MyClass con longitud 0. Por lo tanto, hay una creación de objeto para un objeto que se desechará de inmediato. En la medida en que el código fuente sugiere que el compilador / JIT no puede optimizar este, por lo que no se crea. Además, el uso del objeto de longitud cero produce fundición (es) dentro del método toArray ().
Vea la fuente de ArrayList.toArray ():
Use el primer método para que solo se cree un objeto y evite los moldes (implícitos pero caros).
fuente
new Myclass[0]
es más rápido: shipilev.net/blog/2016/arrays-wisdom-ancientsDe la inspección de JetBrains Intellij Idea:
fuente
Las JVM modernas optimizan la construcción de matriz reflectante en este caso, por lo que la diferencia de rendimiento es pequeña. Nombrar la colección dos veces en ese código repetitivo no es una gran idea, por lo que evitaría el primer método. Otra ventaja de la segunda es que funciona con colecciones sincronizadas y concurrentes. Si desea optimizar, reutilice la matriz vacía (las matrices vacías son inmutables y se pueden compartir), o use un generador de perfiles (!).
fuente
private static final MyClass[] EMPTY_MY_CLASS_ARRAY = new MyClass[0]
no evita que la matriz devuelta se construya por reflexión, pero sí evita que se construya una matriz adicional cada vez.MyClass[] arr = myList.stream().toArray(MyClass[]::new);
¿Ayudaría o perjudicaría con colecciones sincronizadas y concurrentes? ¿Y por qué? Por favor.toArray verifica que la matriz pasada sea del tamaño correcto (es decir, lo suficientemente grande como para ajustarse a los elementos de su lista) y, de ser así, la usa. Por consiguiente, si el tamaño de la matriz lo proporciona más pequeño de lo requerido, se creará una nueva matriz por reflejo.
En su caso, una matriz de tamaño cero es inmutable, por lo que podría elevarse con seguridad a una variable final estática, lo que podría hacer que su código sea un poco más limpio, lo que evita crear la matriz en cada invocación. De todos modos, se creará una nueva matriz dentro del método, por lo que es una optimización de legibilidad.
Podría decirse que la versión más rápida es pasar la matriz de un tamaño correcto, pero a menos que pueda probar que este código es un cuello de botella de rendimiento, prefiera la legibilidad al rendimiento en tiempo de ejecución hasta que se demuestre lo contrario.
fuente
El primer caso es más eficiente.
Eso es porque en el segundo caso:
el tiempo de ejecución realmente crea una matriz vacía (con tamaño cero) y luego dentro del método toArray crea otra matriz para adaptarse a los datos reales. Esta creación se realiza utilizando la reflexión utilizando el siguiente código (tomado de jdk1.5.0_10):
Al usar la primera forma, evita la creación de una segunda matriz y también evita el código de reflexión.
fuente
El segundo es marginalmente más legible, pero hay tan poca mejora que no vale la pena. El primer método es más rápido, sin desventajas en tiempo de ejecución, así que eso es lo que uso. Pero lo escribo de la segunda manera, porque es más rápido escribir. Entonces mi IDE lo marca como una advertencia y ofrece arreglarlo. Con una sola pulsación de tecla, convierte el código del segundo tipo al primero.
fuente
El uso de 'toArray' con la matriz del tamaño correcto funcionará mejor ya que la alternativa creará primero la matriz de tamaño cero y luego la matriz del tamaño correcto. Sin embargo, como usted dice, es probable que la diferencia sea insignificante.
Además, tenga en cuenta que el compilador javac no realiza ninguna optimización. En la actualidad, los compiladores JIT / HotSpot realizan todas las optimizaciones en tiempo de ejecución. No conozco ninguna optimización en torno a 'toArray' en ninguna JVM.
La respuesta a su pregunta, entonces, es en gran medida una cuestión de estilo, pero por razones de coherencia debe formar parte de cualquier estándar de codificación al que se adhiera (ya sea documentado o no).
fuente
código de muestra para entero:
fuente