2011-08-23 13 views
7

¿Cuál consideraría más eficiente?Java Enum valueOf efficiency

El uso de 'WeekDay' es sólo un ejemplo:

public enum WeekDay { 
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY; 
} 

bucle a través y verificar cadena día primero:

public void parseString(String line) { 
    String[] tokens = line.split(); 
    String day = tokens[1]; // day 'should' always be a weekday 
    if (isValidWeekDay(day)) { 
     WeekDay weekDay = WeekDay.valueOf(day); // won't throw exception 
     ... 
    } else { 
     throw new InvalidWeekDayException(day); // subclass of RuntimeException 
    } 
} 
private boolean isValidWeekDay(String day) { 
    for (WeekDay weekDay : WeekDay.values()) { 
     if(weekDay.toString().equals(day)) 
      return true; 
    } 
    return false; 
} 

O ya que en el 99,99% de los casos, el día será correcta:

public void parseString(String line) { 
    String[] tokens = line.split(); 
    String day = tokens[1]; // day 'should' always be a weekday 
    try { 
     WeekDay weekDay = WeekDay.valueOf(day); // might throw exception 
     ... 
    } catch (IllegalArgumentException e) { 
     throw new InvalidWeekDayException(day, e); 
    } 
} 

Actualización:

Para aclarar, la cadena de entrada vendrá de una aplicación cliente, en lugar de un usuario. En otras palabras, sería un error recibir un día no laborable en este ejemplo.

+7

Puede sonar como una respuesta graciosa, pero consideraría el que corrió más rápido cuando lo describí como el más eficiente. –

+0

¿La cadena proviene de algo que ingresa un usuario, o viene todo internamente? – DHall

+0

Yo usaría esto en su lugar, si realmente está haciendo una enumeración de días de la semana: http://joda-time.sourceforge.net/field.html#dayOfWeek – NimChimpsky

Respuesta

4

Sé que es una publicación anterior, pero creo que seguir el resultado seguirá siendo interesante. Ejecuto 10000000 pruebas para encontrar un elemento en enum ENUM {FIRST, SECOND, THIRD, FOURTH, LAST} usando JDK 1.8. La tabla a continuación muestra el tiempo requerido por simple loop y valueOf().

 
text  loop valueOf ratio 
------------------------------ 
"FIRST" 121 65  186% 
"LAST" 188 57  330% 
"foo" 155 8958  1.7% 

Conclusión - Yo no usaría valueOf() si espero que no coincidan con los valores de enumeración.

+0

¿Las pruebas se enfocaron en la eficacia de usar la excepción entonces? ¿O fue la diferencia b/c de algún detalle de implementación en el método valueOf()? ¿Ejecutó esto en una JVM que se ejecuta normalmente (es decir, no en modo de depuración) con JIT habilitado? ¿Y preparó el JIT haciendo una serie de iteraciones antes de que comenzara a usar sus temporizadores?Cuando hice mis pruebas con zxing, vi el tipo de diferencia que está mostrando cuando realicé las pruebas en modo de depuración, pero una vez que el depurador estuvo fuera del camino, el rendimiento fue prácticamente idéntico. –

+0

Hola @Kevin. Hay muchos parámetros que pueden afectar las figuras concretas en la tabla. Tenía curiosidad sobre el "costo de la excepción" en _mi entorno_ (Oracle JVM 1.6 + 64-bit Win7 + modo de depuración). Estaba tan sorprendido por el resultado que decidí publicarlo. Esperaba una relación de 10-20% en el peor de los casos en función de mi experiencia en C++. – aknopov

2

Almacene las cadenas válidas en un HashSet, y decida si una cadena es un día válido o no basado en Set.contains(...).

El conjunto puede ser un static final Set, y se puede envolver en una inmodificable por si acaso:

private static final Map<String> WEEKDAY_STRINGS; 
static { 
    HashSet<String> set = new HashSet(); 
    for (WeekDay d : WeekDay.values()) { 
    set.add(d.toString()); 
    } 
    WEEKDAY_STRINGS = Collections.unmodifiableSet(set); 
} 
+1

esto está complicando demasiado el problema imho – NimChimpsky

2

El bucle no hace nada ese llamado valordel no, tienen la misma funcionalidad: comprobación si su cadena es enum válida. ¿Qué crees que ganas con la primera opción?

La segunda opción es mejor:

try { 
    WeekDay weekDay = WeekDay.valueOf(day); // might throw exception 
     ... 
    } catch (IllegalArgumentException e) { 
     throw new InvalidWeekDayException(day); 
    } 
+0

Supongo que todo se reduce a qué tan comunes son los días inválidos. Si el 50% de los días no son válidos, algunos considerarían muy ineficiente tirar/atrapar una excepción. Sin embargo, si es solo el raro error de la aplicación del cliente, este es definitivamente el camino a seguir. – toolkit

2

O usted podría crear una búsqueda de valores de enumeración dentro de su enumeración, cuando las primeras cargas de clase (véase el modificador static) y validar el uso de get() como se muestra a continuación:

private String dayName; 
private static final Map<String,Weekday> lookup = new HashMap<String, Weekday>(); 
static{ 
    for (Weekday day: values()){ 
     lookup.put(day.dayName, d); 
    } 
} 
public static Weekday get(String _name){ 
    return lookup.get(_name); 
} 

Déjeme saber si usted necesita más detalles

5

Como se ha comentado, tendrá al perfil para salir de dudas. Incluso en su propio enfoque de análisis, puede hacerlo más rápido devolviendo la enumeración cuando analiza la lista.

private WeekDay getValidWeekDay(String day) { 
    for (WeekDay weekDay : WeekDay.values()) { 
     if(weekDay.toString().equals(day)) 
      return weekDay; 
    } 
    return null; 
} 

A menos que este es un momento parte crítica de una aplicación, no me preocupo por eso, en cualquier caso y simplemente tomo el enfoque más legible. Creo que usaría el método WeekDay.valueOf().

Si prefiere no tener que lidiar con excepciones, cree un Mapa de sus valores dentro de la enumeración y haga efectivamente el equivalente de valueOf() desde una búsqueda que devuelve nulo si no se encuentra.

public enum WeekDay { 
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY; 

    private static Map<String, WeekDay> valueMap; 

    public static WeekDay getValue(String possibleName) 
    { 
     if (valueMap == null) 
     { 
      valueMap = new HashMap<String, WeekDay>(); 
      for(WeedDay day: values()) 
       valueMap.put(day.toString(), day); 
     } 
     return valueMap.get(possibleName); 

    } 
} 

Esto es efectivamente lo que el método valueOf() está haciendo de todos modos, excepto que lanza el IllegalArgumentException cuando no se encuentra. Este enfoque simplemente devolverá nulo, y por lo tanto no generará stacktrace.

8

¿Cuál es la preocupación por el rendimiento del segundo enfoque? Atrapar una excepción como esa no cuesta casi nada. El uso de excepciones para el flujo de control normal generalmente es una mala idea desde el punto de vista del diseño, los días en que esto era una consideración de rendimiento han desaparecido.En un depurador, usar excepciones como operaciones de control significativas ralentizará las cosas por un factor de alrededor de 10. Pero esto se optimiza con el JIT y no hay un impacto medible en la producción.

Estos números se basan en la experiencia con una evaluación que hice del proyecto zxing, que utiliza excepciones para todo tipo de control de flujo. Cuando lo vi por primera vez, me horroricé. Todavía creo que no es el mejor diseño, pero hice algunas pruebas y puedo decir con un poco de confianza que no tuvo un impacto real en el rendimiento. Y este es un algoritmo que estaba utilizando excepciones en todo el lugar para el control de flujo. Su situación, donde la excepción solo se producirá en circunstancias muy excepcionales, no es un problema.

Editar: He recibido un voto negativo o dos en mi respuesta, y quiero asegurarme de que soy muy claro en lo que estoy diciendo: no creo que sea una buena idea usar excepciones para flujo de control normal. El hecho de que el rendimiento no sea un buen argumento para no usar excepciones de esta manera no significa que no existan otras razones perfectamente válidas (como la legibilidad, la capacidad de prueba, la capacidad de ampliación). En el caso del OP, el uso de una excepción es absolutamente necesario, y definitivamente no causaría ningún tipo de problema de rendimiento.

+1

Gracias por el consejo Kevin. – toolkit

3

Si su pregunta es realmente sobre la eficiencia de la búsqueda entre 7 elementos, ya ha perdido demasiado tiempo en ella. Incluso los algoritmos de búsqueda más rápidos producen beneficios cero o negativos hasta N> 15 más o menos, aparte del O (1).