2009-05-27 41 views
5

Todos conocemos los efectos que muchas excepciones arrojadas pueden tener sobre el rendimiento de nuestras aplicaciones, por lo tanto, debemos evitar cosas como el uso de excepciones para el flujo de control. Después de esta declaración, debo confesar que al codificar no me importó demasiado sobre esto. He estado trabajando principalmente en la plataforma Java, pero últimamente lo estaba haciendo en la plataforma .NET y descubrí este práctico método: public static bool TryParse(string s,out int result) , que le permite transformar una cadena en int sin generar una excepción. Desde ese momento, sigo usándolo. Solo quería preguntarle acerca de sus preferencias con respecto al uso de public static bool TryParse(string s,out int result) o public static int ToInt32(string value).Convert.ToInt32 versus TryParse

Y desde el punto de vista de Java, simplemente señalando que le falta un método tan similares, a pesar de que podríamos conseguirlo a través de cosas como:

boolean isInteger = Pattern.matches("^\d*$", myString);

Gracias.

Respuesta

7

Sí, a Java le falta un método similar, aunque sin los parámetros out, en realidad es bastante difícil de expresar (aunque se desea devolver un primitivo). En general, sin embargo, en C# debe usar TryParse si espera que el valor no sea un entero a veces, y ToInt32 de lo contrario; de esta manera, la situación "excepcional" se trata como tal.

En particular, si el rendimiento es la razón principal para querer TryParse, el método de correspondencias exógenas que publique es considerablemente peor. El "gasto" de rendimiento de las Excepciones (que es, en realidad, muy mínimo) se ve eclipsado por la cantidad de uso erróneo que pueden hacer para entender fácilmente el flujo de control.

+0

Por cierto, no habría formas alternativas para conseguir que en Java. Uno sería el uso de Integer en lugar de int y el otro para poner int dentro de una matriz y pasarla al método. –

+0

Sí, es por eso que mencioné la parte "mientras queriendo devolver una parte primitiva". Es posible, pero nunca limpio, esencialmente. – Calum

1

Para ello en Java puede usar el bien conocido StringUtils (en el commons-lang), esta clase tiene un método isNumeric

probablemente le puede echar un vistazo al código que esos tipos tienen escritura para that function :

public static boolean isNumeric(String str) { 
    if (str == null) { 
    return false; 
    } 
    int sz = str.length(); 
    for (int i = 0; i < sz; i++) { 
    if (Character.isDigit(str.charAt(i)) == false) { 
     return false; 
    } 
    } 
    return true; 
} 

no estoy diciendo que esta es la manera más eficiente de hacerlo, pero no hay otra alternativa para usted sin el uso de expresiones regulares. ¡Buena suerte!

+1

¿Qué pasa con este material "== falso"? ¡No puedo creer que esté viendo eso en el código fuente de Apache! –

+1

El problema con "== falso" es lo que sucede si accidentalmente usa solo un "=". El tipo de expresión sigue siendo booleano, por lo que se compila pero se termina con un error que podría ser sutil e intermitente. Es solo un buen hábito para salir. –

+0

if (! Character.isDigit (str.charAt (i))) –

3

No sé acerca de C#, pero en Java las excepciones son costosas cuando se lanzan, pero son muy caras. Si espera que una fracción significativa de las cadenas no sea válida, vale la pena validarlas primero, incluso si usa una expresión regular.

Pero no use String.matches() o Pattern.matches() para aplicar la expresión regular; esos métodos recompilan la expresión regular cada vez que los llamas. En su lugar, compila la expresión regular antes de tiempo y guárdala como un objeto Patrón, luego haz tu validación con eso. En mis pruebas, al analizar una lista de 10.000 cadenas de las cuales el 20% no eran válidas, la validación previa con un Patrón es casi el doble de rápida que con el uso del Integer.parseInt() solo y captando las excepciones.

Sin embargo, esta explicación solo se aplica si realiza muchas conversiones en un ciclo cerrado. Si solo los está haciendo de vez en cuando, como cuando acepta la entrada del usuario, dejar que Integer.parseInt() haga la validación está bien. Y si elige validar con una expresión regular, necesitará una expresión regular mucho mejor que ^\d*$ - esa expresión regular coincidirá con la cadena vacía, así como con los "números" mayores que Integer.MAX_VALUE, y no coincidirá en absoluto con los números negativos.

0

Sólo quería preguntarle acerca de sus preferencias con respecto al uso de bool público estático TryParse (string s, out int result) o público static int ToInt32 (valor de cadena).

Sí, utilizo TryParse excepto cuando espero que el valor de siempre sea válida. Encuentro que se lee más limpio que usando las excepciones. Incluso si quiero una excepción, generalmente quiero personalizar el mensaje o lanzar mi propia excepción personalizada; por lo tanto, utilizo TryParse y lanzo una excepción manualmente.

En Java y C#, intento capturar el mínimo conjunto de excepciones posibles. En Java, eso significa que tengo que capturar NullPointerException y NumberFormatException por separado en respuesta a Number.ValueOf (...); alternativamente, puedo atrapar "Excepción" y arriesgarme a atrapar algo involuntariamente. Con TryParse en C#, no me preocupo por eso en absoluto.

1

Y desde el punto de vista de Java, simplemente señalando que le falta un método similar tal , a pesar de que podíamos conseguir a través de cosas como:

boolean isInteger = Pattern.matches("^\d*$", myString); 

predecir si Integer.parseInt(myString) lanzaría una Excepción, hay más trabajo por hacer. The String podría comenzar con un -. Además, un int no puede tener más de 10 dígitos significativos. Entonces, una expresión más confiable sería ^-?0*\d{1,10}$. Pero incluso esta expresión no predeciría todas las excepciones porque todavía es demasiado impreciso.

Para generar una expresión regular confiable es posible. Pero sería muy largo. También es posible implementar un método que determina con precisión si parseInt arrojaría una excepción. Se podría tener este aspecto:

static boolean wouldParseIntThrowException(String s) { 
    if (s == null || s.length() == 0) { 
     return true; 
    } 

    char[] max = Integer.toString(Integer.MAX_VALUE).toCharArray(); 
    int i = 0, j = 0, len = s.length(); 
    boolean maybeOutOfBounds = true; 

    if (s.charAt(0) == '-') { 
     if (len == 1) { 
      return true; // s == "-" 
     } 
     i = 1; 
     max[max.length - 1]++; // 2147483647 -> 2147483648 
    } 
    while (i < len && s.charAt(i) == '0') { 
     i++; 
    } 
    if (max.length < len - i) { 
     return true; // too long/out of bounds 
    } else if (len - i < max.length) { 
     maybeOutOfBounds = false; 
    } 
    while (i < len) { 
     char digit = s.charAt(i++); 
     if (digit < '0' || '9' < digit) { 
      return true; 
     } else if (maybeOutOfBounds) { 
      char maxdigit = max[j++]; 
      if (maxdigit < digit) { 
       return true; // out of bounds 
      } else if (digit < maxdigit) { 
       maybeOutOfBounds = false; 
      } 
     } 
    } 
    return false; 
} 

No sé qué versión es más eficiente sin embargo. Y depende principalmente del contexto qué tipo de controles son razonables.

En C# a compruebe si se puede convertir una cadena, utilizaría TryParse. Y si devuelve verdadero, entonces como subproducto, obtuvo convertido al mismo tiempo. Esta es una característica clara y no veo un problema con solo volver a implementar parseInt para devolver nulo en lugar de lanzar una excepción.

Pero si no quiere volver a implementar el método de análisis, podría ser bueno tener a mano un conjunto de métodos que pueda usar según la situación. Ellos podrían este aspecto:

private static Pattern QUITE_ACCURATE_INT_PATTERN = Pattern.compile("^-?0*\\d{1,10}$"); 

static Integer tryParseIntegerWhichProbablyResultsInOverflow(String s) { 
    Integer result = null; 
    if (!wouldParseIntThrowException(s)) { 
     try { 
      result = Integer.parseInt(s); 
     } catch (NumberFormatException ignored) { 
      // never happens 
     } 
    } 
    return result; 
} 

static Integer tryParseIntegerWhichIsMostLikelyNotEvenNumeric(String s) { 
    Integer result = null; 
    if (s != null && s.length() > 0 && QUITE_ACCURATE_INT_PATTERN.matcher(s).find()) { 
     try { 
      result = Integer.parseInt(s); 
     } catch (NumberFormatException ignored) { 
     // only happens if the number is too big 
     } 
    } 
    return result; 
} 

static Integer tryParseInteger(String s) { 
    Integer result = null; 
    if (s != null && s.length() > 0) { 
     try { 
      result = Integer.parseInt(s); 
     } catch (NumberFormatException ignored) { 
     } 
    } 
    return result; 
} 

static Integer tryParseIntegerWithoutAnyChecks(String s) { 
    try { 
     return Integer.parseInt(s); 
    } catch (NumberFormatException ignored) { 
    } 
    return null; 
} 
Cuestiones relacionadas