Tengo un bucle que se parece a esto:
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
Este es el contenido principal de un método cuyo único propósito es devolver la matriz de flotadores. Quiero que este método regrese null
si hay un error, así que pongo el bucle dentro de un try...catch
bloque, así:
try {
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
} catch (NumberFormatException ex) {
return null;
}
Pero también pensé en poner el try...catch
bloque dentro del bucle, así:
for (int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
} catch (NumberFormatException ex) {
return null;
}
myFloats[i] = myNum;
}
¿Hay alguna razón, rendimiento o de otro tipo, para preferir uno sobre el otro?
Editar: El consenso parece ser que es más limpio poner el bucle dentro del try / catch, posiblemente dentro de su propio método. Sin embargo, todavía hay debate sobre cuál es más rápido. ¿Alguien puede probar esto y volver con una respuesta unificada?
java
performance
loops
try-catch
Michael Myers
fuente
fuente
Respuestas:
ACTUACIÓN:
No hay absolutamente ninguna diferencia de rendimiento en el lugar donde se colocan las estructuras try / catch. Internamente, se implementan como una tabla de rango de código en una estructura que se crea cuando se llama al método. Mientras se ejecuta el método, las estructuras try / catch están completamente fuera de la imagen a menos que ocurra un lanzamiento, entonces la ubicación del error se compara con la tabla.
Aquí hay una referencia: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
La tabla se describe a mitad de camino hacia abajo.
fuente
Actuación : como dijo Jeffrey en su respuesta, en Java no hace mucha diferencia.
Generalmente , para facilitar la lectura del código, su elección de dónde capturar la excepción depende de si desea que el bucle siga procesándose o no.
En su ejemplo, regresó al detectar una excepción. En ese caso, pondría el try / catch alrededor del bucle. Si simplemente desea atrapar un valor malo pero continuar con el procesamiento, colóquelo dentro.
La tercera forma : siempre puede escribir su propio método ParseFloat estático y hacer que el manejo de excepciones se aborde en ese método en lugar de su bucle. ¡Hacer el manejo de excepciones aislado del bucle en sí!
fuente
Muy bien, después de que Jeffrey L Whitledge dijo que no había diferencia de rendimiento (a partir de 1997), fui y lo probé. Ejecuté este pequeño punto de referencia:
Verifiqué el bytecode resultante usando javap para asegurarme de que nada estuviera en línea.
Los resultados mostraron que, suponiendo optimizaciones JIT insignificantes, Jeffrey tiene razón ; No hay absolutamente ninguna diferencia de rendimiento en Java 6, VM cliente Sun (no tenía acceso a otras versiones). La diferencia de tiempo total es del orden de unos pocos milisegundos durante toda la prueba.
Por lo tanto, la única consideración es lo que parece más limpio. Creo que la segunda forma es fea, así que me quedaré con la primera o con Ray Hayes .
fuente
Si bien el rendimiento puede ser el mismo y lo que "se ve" mejor es muy subjetivo, todavía hay una gran diferencia en la funcionalidad. Tome el siguiente ejemplo:
El ciclo while está dentro del bloque try catch, la variable 'j' se incrementa hasta llegar a 40, se imprime cuando j mod 4 es cero y se lanza una excepción cuando j llega a 20.
Antes de cualquier detalle, aquí el otro ejemplo:
La misma lógica que la anterior, la única diferencia es que el bloque try / catch está ahora dentro del ciclo while.
Aquí viene la salida (mientras está en try / catch):
Y la otra salida (try / catch in while):
Ahí tienes una diferencia bastante significativa:
mientras que en try / catch se escapa del ciclo
intentar / atrapar mientras mantiene el bucle activo
fuente
try{} catch() {}
el bucle si el bucle se trata como una sola "unidad" y al encontrar un problema en esa unidad hace que toda la "unidad" (o bucle en este caso) vaya hacia el sur. Pongatry{} catch() {}
dentro del bucle si está tratando una única iteración del bucle, o un solo elemento en una matriz como una "unidad" completa. Si ocurre una excepción en esa "unidad", simplemente omitimos ese elemento y luego continuamos con la siguiente iteración del ciclo.Estoy de acuerdo con todas las publicaciones de rendimiento y legibilidad. Sin embargo, hay casos en los que realmente importa. Otras personas mencionaron esto, pero podría ser más fácil de ver con ejemplos.
Considere este ejemplo ligeramente modificado:
Si desea que el método parseAll () devuelva nulo si hay algún error (como en el ejemplo original), colocaría el try / catch en el exterior de esta manera:
En realidad, probablemente debería devolver un error aquí en lugar de nulo, y en general no me gusta tener devoluciones múltiples, pero se entiende la idea.
Por otro lado, si desea que simplemente ignore los problemas y analice las cadenas que pueda, colocará el try / catch en el interior del bucle de esta manera:
fuente
Como ya se mencionó, el rendimiento es el mismo. Sin embargo, la experiencia del usuario no es necesariamente idéntica. En el primer caso, fallará rápidamente (es decir, después del primer error), sin embargo, si coloca el bloque try / catch dentro del bucle, puede capturar todos los errores que se crearían para una llamada dada al método. Al analizar una matriz de valores de cadenas en las que espera algunos errores de formato, definitivamente hay casos en los que le gustaría poder presentar todos los errores al usuario para que no tenga que intentar corregirlos uno por uno .
fuente
Si falla todo o nada, entonces el primer formato tiene sentido. Si desea poder procesar / devolver todos los elementos que no fallan, debe usar el segundo formulario. Esos serían mis criterios básicos para elegir entre los métodos. Personalmente, si es todo o nada, no usaría la segunda forma.
fuente
Siempre y cuando sepa lo que necesita lograr en el ciclo, puede poner el try catch fuera del ciclo. Pero es importante comprender que el ciclo finalizará tan pronto como ocurra la excepción y que no siempre sea lo que desea. Esto es realmente un error muy común en el software basado en Java. Las personas necesitan procesar una serie de elementos, como vaciar una cola y confiar falsamente en una declaración externa try / catch que maneje todas las excepciones posibles. También podrían estar manejando solo una excepción específica dentro del ciclo y no esperar que ocurra ninguna otra excepción. Luego, si se produce una excepción que no se maneja dentro del ciclo, entonces el ciclo se "compensará", posiblemente finalice prematuramente y la instrucción catch externa maneja la excepción.
Si el ciclo tenía como función en la vida vaciar una cola, es muy probable que ese ciclo finalice antes de que esa cola se vacíe realmente. Falla muy común.
fuente
En sus ejemplos no hay diferencia funcional. Encuentro tu primer ejemplo más legible.
fuente
Debería preferir la versión externa sobre la versión interna. Esta es solo una versión específica de la regla, mueve cualquier cosa fuera del ciclo que puedas mover fuera del ciclo. Dependiendo del compilador IL y el compilador JIT, sus dos versiones pueden o no tener diferentes características de rendimiento.
En otra nota, probablemente deberías mirar float. TryParse o Convert.ToFloat.
fuente
Si coloca el try / catch dentro del bucle, seguirá haciendo bucles después de una excepción. Si lo coloca fuera del bucle, se detendrá tan pronto como se produzca una excepción.
fuente
Mi perspectiva sería que los bloques try / catch son necesarios para asegurar un manejo adecuado de las excepciones, pero crear tales bloques tiene implicaciones de rendimiento. Como los bucles contienen cálculos repetitivos intensivos, no se recomienda poner bloques try / catch dentro de los bucles. Además, parece que donde ocurre esta condición, a menudo se detecta "Excepción" o "RuntimeException". Se debe evitar la excepción RuntimeException atrapada en el código. Nuevamente, si trabaja en una gran empresa, es esencial registrar esa excepción correctamente o detener la ejecución de la excepción de tiempo de ejecución. Todo el punto de esta descripción es
PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
fuente
configurar un marco de pila especial para el try / catch agrega una sobrecarga adicional, pero la JVM puede detectar el hecho de que está regresando y optimizar esto.
Dependiendo del número de iteraciones, la diferencia de rendimiento probablemente será insignificante.
Sin embargo, estoy de acuerdo con los demás en que tenerlo fuera del bucle hace que el cuerpo del bucle se vea más limpio.
Si existe la posibilidad de que alguna vez desee continuar con el procesamiento en lugar de salir si hay un número no válido, entonces querrá que el código esté dentro del bucle.
fuente
Si está adentro, entonces ganará la sobrecarga de la estructura try / catch N veces, en lugar de solo una vez en el exterior.
Cada vez que se llama a una estructura Try / Catch, agrega una sobrecarga a la ejecución del método. Solo un poco de memoria y procesadores necesarios para lidiar con la estructura. Si está ejecutando un bucle 100 veces, y por razones hipotéticas, digamos que el costo es de 1 tic por llamada de prueba / captura, entonces tener el Try / Catch dentro del bucle le cuesta 100 tics, en lugar de solo 1 tick si es fuera del circuito.
fuente
El punto principal de las excepciones es fomentar el primer estilo: permitir que el manejo de errores se consolide y se maneje una vez, no inmediatamente en cada sitio de error posible.
fuente
ponlo dentro. Puede seguir procesando (si lo desea) o puede lanzar una excepción útil que le dice al cliente el valor de myString y el índice de la matriz que contiene el valor incorrecto. Creo que NumberFormatException ya le dirá el valor incorrecto, pero el principio es colocar todos los datos útiles en las excepciones que arroje. Piense en lo que sería interesante para usted en el depurador en este punto del programa.
Considerar:
En el momento de necesidad, realmente apreciará una excepción como esta con tanta información como sea posible.
fuente
Me gustaría agregar la mía
0.02c
sobre dos consideraciones competitivas cuando analizo el problema general de dónde ubicar el manejo de excepciones:La responsabilidad "más amplia" del
try-catch
bloque (es decir, fuera del bucle en su caso) significa que al cambiar el código en algún momento posterior, puede agregar por error una línea que es manejada por sucatch
bloque existente ; posiblemente sin querer. En su caso, esto es menos probable porque está capturando explícitamente unNumberFormatException
Cuanto más "estrecha" sea la responsabilidad del
try-catch
bloque, más difícil será la refactorización. Particularmente cuando (como en su caso) está ejecutando una instrucción "no local" desde dentro delcatch
bloque (lareturn null
declaración).fuente
Eso depende del manejo de fallas. Si solo quiere omitir los elementos de error, intente adentro:
En cualquier otro caso, preferiría probar afuera. El código es más legible, es más limpio. Tal vez sería mejor lanzar una IllegalArgumentException en el caso de error en lugar de devolver nulo.
fuente
Pondré mis $ 0.02. A veces terminas necesitando agregar un "finalmente" más adelante en tu código (porque ¿quién escribe su código perfectamente la primera vez?). En esos casos, de repente tiene más sentido tener el try / catch fuera del ciclo. Por ejemplo:
Porque si obtiene un error, o no, solo desea liberar su conexión de base de datos (o elegir su tipo favorito de otro recurso ...) una vez.
fuente
Otro aspecto no mencionado en lo anterior es el hecho de que cada try-catch tiene alguna impacto en la pila, lo que puede tener implicaciones para los métodos recursivos.
Si el método "exterior ()" llama al método "interno ()" (que puede llamarse a sí mismo de forma recursiva), intente localizar el try-catch en el método "externo ()" si es posible. Un ejemplo simple de "bloqueo de pila" que utilizamos en una clase de rendimiento falla a unos 6.400 cuadros cuando el try-catch está en el método interno, y a unos 11.600 cuando está en el método externo.
En el mundo real, esto puede ser un problema si está utilizando el patrón Compuesto y tiene estructuras anidadas grandes y complejas.
fuente
Si desea capturar la Excepción para cada iteración, o verificar en qué iteración se produce la Excepción y capturar todas las Excepciones en una iteración, coloque intentar ... atrapar dentro del ciclo. Esto no interrumpirá el ciclo si ocurre una Excepción y puede capturar cada Excepción en cada iteración a lo largo del ciclo.
Si desea romper el ciclo y examinar la excepción cada vez que se lanza, use try ... catch fuera del ciclo. Esto romperá el ciclo y ejecutará declaraciones después de catch (si las hay).
Todo depende de tu necesidad. Prefiero usar try ... catch dentro del bucle durante la implementación ya que, si se produce una excepción, los resultados no son ambiguos y el bucle no se romperá y ejecutará por completo.
fuente