java - enumerado - ¿hay un impacto en el rendimiento cuando se usan enum.values() vs. arreglos de cadenas?
iterate enum c# (7)
Como regla general: antes de pensar en optimizar, ¿tiene alguna idea de que este código pueda ralentizar su aplicación?
Ahora, los hechos.
enumeración son, en gran parte, azúcar sintáctica dispersa a través del proceso de compilación. Como consecuencia, el método de valores, definido para una clase de enumeración, devuelve una colección estática (es decir, cargada en la inicialización de la clase) con rendimientos que pueden considerarse aproximadamente equivalentes a una matriz.
Estoy usando enumeraciones para reemplazar constantes de String
en mi aplicación java (JRE 1.5).
¿Hay un impacto en el rendimiento cuando trato la enumeración como una matriz estática de nombres en un método que se llama constantemente (por ejemplo, al representar la interfaz de usuario)?
Mi código se parece un poco a esto:
public String getValue(int col) {
return ColumnValues.values()[col].toString();
}
Aclaraciones:
- Me preocupa un costo oculto relacionado con la enumeración de
values()
repetidamente (por ejemplo, dentro de los métodos de paint ()). - Ahora puedo ver que todos mis escenarios incluyen una conversión
int
=>enum
, que no es la forma de Java.
¿Cuál es el precio real de extraer la matriz de values()
? ¿Es incluso un problema?
Desarrolladores de Android
Lea la respuesta de Simon Langhoff a continuación, que ha señalado anteriormente Geeks On Hugs en los comentarios de la respuesta aceptada. Enum.values()
debe hacer una copia defensiva
Creo que sí. Y es más conveniente usar constantes.
Para las enumeraciones, para mantener la inmutabilidad, clonan la matriz de respaldo cada vez que llama al método Values (). Esto significa que tendrá un impacto en el rendimiento. ¿Cuánto depende de su escenario específico.
¡He estado monitoreando mi propia aplicación de Android y descubrí que esta simple llamada usaba 13.4% de tiempo de CPU! en mi caso especifico
Para evitar la clonación de la matriz de valores, decidí guardar en caché los valores como un campo privado y luego hacer un bucle a través de esos valores cuando sea necesario:
private final static Protocol[] values = Protocol.values();
Después de esta pequeña optimización, mi llamada al método solo acaparó un tiempo de CPU insignificante de 0.0%
En mi caso de uso, esta fue una optimización bienvenida, sin embargo, es importante tener en cuenta que usar este enfoque es un compromiso de mutabilidad de su enumeración. ¡¿Quién sabe lo que la gente podría poner en tu matriz de valores una vez que les des una referencia ?!
Si está iterando a través de sus valores de enumeración solo para buscar un valor específico, puede asignar de forma estática los valores de enumeración a números enteros. Esto empuja el impacto en el rendimiento en la carga de clases, y hace que sea fácil / bajo impacto obtener valores de enumeración específicos basados en un parámetro asignado.
public enum ExampleEnum {
value1(1),
value2(2),
valueUndefined(Integer.MAX_VALUE);
private final int enumValue;
private static Map enumMap;
ExampleEnum(int value){
enumValue = value;
}
static {
enumMap = new HashMap<Integer, ExampleEnum>();
for (ExampleEnum exampleEnum: ExampleEnum.values()) {
enumMap.put(exampleEnum.value, exampleEnum);
}
}
public static ExampleEnum getExampleEnum(int value) {
return enumMap.contains(value) ? enumMap.get(value) : valueUndefined;
}
}
Si le preocupa el rendimiento, mida.
Desde el código, no esperaría ninguna sorpresa, pero el 90% de todas las conjeturas de rendimiento son incorrectas. Si desea estar seguro, considere la posibilidad de subir las enumeraciones al código de llamada (es decir, public String getValue(ColumnValues value) {return value.toString();}
).
utilizar esta:
private enum ModelObject { NODE, SCENE, INSTANCE, URL_TO_FILE, URL_TO_MODEL,
ANIMATION_INTERPOLATION, ANIMATION_EVENT, ANIMATION_CLIP, SAMPLER, IMAGE_EMPTY,
BATCH, COMMAND, SHADER, PARAM, SKIN }
private static final ModelObject int2ModelObject[] = ModelObject.values();
Enum.values()
le proporciona una referencia a una matriz, y la iteración de una matriz de enumeraciones cuesta lo mismo que la iteración de una matriz de cadenas. Mientras tanto, la comparación de valores de enumeración con otros valores de enumeración puede ser más rápida que la comparación de cadenas con cadenas.
Mientras tanto, si está preocupado por el costo de invocar el método de values()
en lugar de tener una referencia a la matriz, no se preocupe. La invocación de métodos en Java es (ahora) increíblemente rápida, y cada vez que realmente importa para el rendimiento, la invocación del método será incorporada por el compilador de todos modos.
Entonces, en serio, no te preocupes por eso. Concéntrese en la legibilidad del código y use Enum
para que el compilador lo atrape si alguna vez intenta usar un valor constante que su código no esperaba manejar.
Si tienes curiosidad acerca de por qué las comparaciones de enumeración pueden ser más rápidas que las comparaciones de cadenas, aquí están los detalles:
Depende de si las cadenas han sido interned o no. Para los objetos Enum
, siempre hay solo una instancia de cada valor de enumeración en el sistema, por lo que cada llamada a Enum.equals()
se puede hacer muy rápidamente, como si estuviera usando el operador ==
lugar de los equals()
método. De hecho, con los objetos Enum
, es seguro usar ==
lugar de equals()
, mientras que no es seguro hacerlo con cadenas.
Para las cadenas, si las cadenas han sido internadas, entonces la comparación es tan rápida como con un Enum
. Sin embargo, si las cadenas no han sido internadas, entonces el método String.equals()
realmente necesita recorrer la lista de caracteres en ambas cadenas hasta que una de las cadenas termine o descubra un carácter que sea diferente entre las dos cadenas.
Pero, de nuevo, es probable que esto no importe, incluso en el código de renderizado Swing que debe ejecutarse rápidamente. :-)
@Ben Lings señala que Enum.values()
debe hacer una copia defensiva, ya que las matrices son mutables y es posible que pueda reemplazar un valor en la matriz que devuelve Enum.values()
. Esto significa que usted tiene que considerar el costo de esa copia defensiva. Sin embargo, copiar una única matriz contigua es generalmente una operación rápida, asumiendo que se implementa "bajo el capó" utilizando algún tipo de llamada de memoria-copia, en lugar de iterar ingenuamente sobre los elementos de la matriz. Entonces, no creo que eso cambie la respuesta final aquí.