values - foreach enum java
Enum.values() vs EnumSet.allOf(). ¿Cuál es más preferible? (6)
Busqué EnumSet.allOf
y parece muy eficiente, especialmente para enumeraciones con menos de 64 valores.
Básicamente, todos los conjuntos comparten la única matriz de todos los valores de enum posibles y la única otra información es una máscara de bits que en el caso de allOf
se configura de una sola vez.
Por otro lado, Enum.values () parece ser un poco de magia negra. Además, devuelve una matriz, no una colección, por lo que en muchos casos debe decorarse con Arrays.asList () para que se pueda utilizar en cualquier lugar que espere la recopilación.
Entonces, ¿debería EnumSet.allOf
ser más preferible a Enum.values
?
Más específicamente, qué forma de for
iterator debería usarse:
for ( final MyEnum val: MyEnum.values( ) );
o
for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
Debe usar el enfoque más simple y claro para usted. El rendimiento no debe ser una consideración en la mayoría de las situaciones.
En mi humilde opinión: ninguna opción funciona muy bien, ya que ambos crean objetos. Uno en el primer caso y tres en el segundo. Podría construir una constante que contenga todos los valores por razones de rendimiento.
Debido a que no recibí la respuesta a mi pregunta sobre cuál es más eficiente, he decidido hacer algunas pruebas por mi cuenta.
He probado la iteración sobre los values()
, Arrays.asList( values() )
y EnumSet.allOf( )
. He repetido estas pruebas 10,000,000 veces para diferentes tamaños de enum. Aquí están los resultados de la prueba:
oneValueEnum_testValues 1.328
oneValueEnum_testList 1.687
oneValueEnum_testEnumSet 0.578
TwoValuesEnum_testValues 1.360
TwoValuesEnum_testList 1.906
TwoValuesEnum_testEnumSet 0.797
ThreeValuesEnum_testValues 1.343
ThreeValuesEnum_testList 2.141
ThreeValuesEnum_testEnumSet 1.000
FourValuesEnum_testValues 1.375
FourValuesEnum_testList 2.359
FourValuesEnum_testEnumSet 1.219
TenValuesEnum_testValues 1.453
TenValuesEnum_testList 3.531
TenValuesEnum_testEnumSet 2.485
TwentyValuesEnum_testValues 1.656
TwentyValuesEnum_testList 5.578
TwentyValuesEnum_testEnumSet 4.750
FortyValuesEnum_testValues 2.016
FortyValuesEnum_testList 9.703
FortyValuesEnum_testEnumSet 9.266
Estos son los resultados de las pruebas ejecutadas desde la línea de comandos. Cuando realicé estas pruebas desde Eclipse, obtuve un apoyo abrumador para testValues
. Básicamente, era más pequeño que EnumSet
incluso para enums pequeños. Creo que la ganancia de rendimiento proviene de la optimización del iterador de matriz en el ciclo for ( val : array )
.
Por otro lado, tan pronto como necesite pasar java.util.Collection, Arrays.asList( )
pierde en EnumSet.allOf
, especialmente para las enumeraciones pequeñas, que creo que serán la mayoría en cualquier código base.
Entonces, yo diría que deberías usar
for ( final MyEnum val: MyEnum.values( ) )
pero
Iterables.filter(
EnumSet.allOf( MyEnum.class ),
new Predicate< MyEnum >( ) {...}
)
Y solo use Arrays.asList( MyEnum.values( ) )
donde java.util.List
es absolutamente necesario.
El método de values()
es más claro y eficiente si solo desea iterar sobre todos los valores enum posibles. Los valores son almacenados en caché por la clase (ver Class.getEnumConstants()
)
Si necesita un subconjunto de valores, debe usar un EnumSet
. Comience con allOf()
o noneOf()
y agregue o elimine valores o use solo of()
como lo necesite.
No es que haya pasado por toda la implementación, pero me parece que EnumSet.allOf () básicamente está usando la misma infraestructura que .values (). Por lo tanto, esperaría que EnumSet.allOf () requiera algunos pasos adicionales (probablemente insignificantes) (consulte http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988 ).
Me parece claro que el uso previsto de foreach es for(MyEnum val : MyEnum.values())
¿por qué hacerlo de manera diferente? Solo confundirá al programador de mantenimiento.
Quiero decir, si necesitas una colección, deberías obtener una. Si desea utilizar un foreach, las matrices son lo suficientemente buenas. ¡Prefiero las matrices si me presionan! ¿Por qué envolver algo con algo, si lo que tienes (matriz) es lo suficientemente bueno? Las cosas simples son normalmente más rápidas.
De todos modos, Peter Lawrey tiene razón. No se preocupe por el rendimiento de esto ... Es lo suficientemente rápido, y es probable que haya millones de otros cuellos de botella que hacen que esa pequeña diferencia de rendimiento teórico sea totalmente irrelevante (sin embargo, no veo su punto de "creación de objetos"). el ejemplo parece estar 100% bien).
También hay Class.getEnumConstants()
bajo el capó todos llaman métodos values()
de tipos enum de todos modos, a través de la reflexión .
EnumSet
no está construido con la intención de iterar sobre sus valores. Más bien se implementa con la idea de que represente un BitMap o BitMask de manera eficiente (o razonablemente eficiente). El javadoc en EnumSet
también declara:
Los conjuntos Enum están representados internamente como vectores de bits. Esta representación es extremadamente compacta y eficiente. El rendimiento de espacio y tiempo de esta clase debería ser lo suficientemente bueno como para permitir su uso como una alternativa de alta calidad y segura para tipos de "indicadores de bits" tradicionales basados en int. Incluso las operaciones masivas (como containsAll y retainAll) deberían ejecutarse muy rápidamente si su argumento también es un conjunto de enumeración.
Como solo un bit único puede representar un cierto valor de Enum, también se implementa como un Set
y no como una List
.
Ahora, probablemente también sea cierto que puede lograr lo mismo, y más rápido, usando máscaras de bits estilo C (x ^ 2), sin embargo, ofrece un estilo de codificación más intuitivo y escribe un uso seguro usando enumeraciones, y se expande fácilmente más allá del tamaño de lo que puede contener un int
o un long
Como tal, puede probar que todos los bits se establecen de la siguiente manera:
public class App {
enum T {A,B}
public static void main(String [] args) {
EnumSet<T> t = EnumSet.of(T.A);
t.containsAll(EnumSet.allOf(T.class));
}
}