캐치 루프 내부 또는 외부로 이동해야합니까?
다음과 같은 루프가 있습니다.
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
이것은 부동 소수점 배열을 반환하는 유일한 목적을 가진 메소드의 주요 내용입니다. null
오류가있는 경우이 메소드가 반환되기를 원 하므로 루프를 다음 try...catch
과 같이 블록 안에 넣습니다 .
try {
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
} catch (NumberFormatException ex) {
return null;
}
그러나 다음 try...catch
과 같이 루프 안에 블록 을 넣는 것을 생각 했습니다.
for (int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
} catch (NumberFormatException ex) {
return null;
}
myFloats[i] = myNum;
}
다른 것을 선호하는 이유, 성능 또는 기타가 있습니까?
편집 : 합의는 try / catch 내부에 루프를 넣는 것이 더 깨끗한 것 같습니다. 그러나 여전히 어느 쪽이 더 빠른지에 대한 논쟁이 있습니다. 누군가 이것을 테스트하고 통일 된 답변으로 돌아올 수 있습니까?
공연:
try / catch 구조의 위치에 성능 차이는 전혀 없습니다. 내부적으로는 메소드가 호출 될 때 작성되는 구조에서 코드 범위 테이블로 구현됩니다. 메소드가 실행되는 동안 throw / catch 구조는 throw가 발생하지 않는 한 완전히 그림에서 벗어난 것이므로 오류 위치는 테이블과 비교됩니다.
다음은 참조입니다 : http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
표는 반쯤 아래로 설명되어 있습니다.
성능 : Jeffrey가 답장에서 말했듯이 Java에서는 큰 차이가 없습니다.
일반적으로 코드의 가독성을 위해 예외를 포착 할 위치를 선택하는 것은 루프 처리를 유지할지 여부에 따라 다릅니다.
귀하의 예에서 예외를 잡으면 돌아 왔습니다. 이 경우 루프 주위에 try / catch를 배치합니다. 단순히 나쁜 가치를 포착하고 가공을 계속하고 싶다면 그것을 안에 넣으십시오.
세 번째 방법 : 항상 자신의 정적 ParseFloat 메소드를 작성하고 루프가 아닌 해당 메소드에서 예외 처리를 처리 할 수 있습니다. 예외 처리를 루프 자체에 격리시킵니다!
class Parsing
{
public static Float MyParseFloat(string inputValue)
{
try
{
return Float.parseFloat(inputValue);
}
catch ( NumberFormatException e )
{
return null;
}
}
// .... your code
for(int i = 0; i < max; i++)
{
String myString = ...;
Float myNum = Parsing.MyParseFloat(myString);
if ( myNum == null ) return;
myFloats[i] = (float) myNum;
}
}
Jeffrey L Whitledge 가 성능 차이가 없다고 말한 후 (1997 년 기준) 테스트를 진행했습니다. 이 작은 벤치 마크를 실행했습니다.
public class Main {
private static final int NUM_TESTS = 100;
private static int ITERATIONS = 1000000;
// time counters
private static long inTime = 0L;
private static long aroundTime = 0L;
public static void main(String[] args) {
for (int i = 0; i < NUM_TESTS; i++) {
test();
ITERATIONS += 1; // so the tests don't always return the same number
}
System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
}
public static void test() {
aroundTime += testAround();
inTime += testIn();
}
public static long testIn() {
long start = System.nanoTime();
Integer i = tryInLoop();
long ret = System.nanoTime() - start;
System.out.println(i); // don't optimize it away
return ret;
}
public static long testAround() {
long start = System.nanoTime();
Integer i = tryAroundLoop();
long ret = System.nanoTime() - start;
System.out.println(i); // don't optimize it away
return ret;
}
public static Integer tryInLoop() {
int count = 0;
for (int i = 0; i < ITERATIONS; i++) {
try {
count = Integer.parseInt(Integer.toString(count)) + 1;
} catch (NumberFormatException ex) {
return null;
}
}
return count;
}
public static Integer tryAroundLoop() {
int count = 0;
try {
for (int i = 0; i < ITERATIONS; i++) {
count = Integer.parseInt(Integer.toString(count)) + 1;
}
return count;
} catch (NumberFormatException ex) {
return null;
}
}
}
javap를 사용하여 결과 바이트 코드를 검사하여 아무것도 인라인되지 않았는지 확인했습니다.
결과는 중요하지 않은 JIT 최적화를 가정하면 Jeffrey가 정확 하다는 것을 보여주었습니다 . Java 6, Sun 클라이언트 VM에는 성능 차이 가 전혀 없습니다 (다른 버전에는 액세스 할 수 없었습니다). 총 시간 차이는 전체 테스트에서 몇 밀리 초 정도입니다.
따라서 유일한 고려 사항은 가장 깨끗하게 보이는 것입니다. 나는 두 번째 방법이 못 생겼다는 것을 알았으므로 첫 번째 방법이나 Ray Hayes의 방법 중 하나를 고수 할 것 입니다.
성능은 동일 할 수 있으며 "보이는"것이 더 주관적이지만 기능에는 여전히 큰 차이가 있습니다. 다음 예제를 보자.
Integer j = 0;
try {
while (true) {
++j;
if (j == 20) { throw new Exception(); }
if (j%4 == 0) { System.out.println(j); }
if (j == 40) { break; }
}
} catch (Exception e) {
System.out.println("in catch block");
}
while 루프는 try catch 블록 안에 있으며 변수 'j'는 40에 도달 할 때까지 증가하고 j mod 4가 0이면 인쇄되고 j가 20에 도달하면 예외가 발생합니다.
세부 사항 전에 다른 예는 다음과 같습니다.
Integer i = 0;
while (true) {
try {
++i;
if (i == 20) { throw new Exception(); }
if (i%4 == 0) { System.out.println(i); }
if (i == 40) { break; }
} catch (Exception e) { System.out.println("in catch block"); }
}
위와 동일한 논리, 차이점은 try / catch 블록이 while 루프 안에 있다는 것입니다.
다음은 try / catch 동안 출력입니다.
4
8
12
16
in catch block
그리고 다른 출력 (시도 / 잡기) :
4
8
12
16
in catch block
24
28
32
36
40
거기에는 상당한 차이가 있습니다.
try / catch가 루프를 벗어나는 동안
루프를 활성 상태로 유지하면서 try / catch in
모든 성능 및 가독성 게시물에 동의합니다. 그러나 실제로 중요한 경우가 있습니다. 다른 사람들이 이것을 언급했지만 예제를 통해 더 쉽게 볼 수 있습니다.
이 약간 수정 된 예를 고려하십시오.
public static void main(String[] args) {
String[] myNumberStrings = new String[] {"1.2345", "asdf", "2.3456"};
ArrayList asNumbers = parseAll(myNumberStrings);
}
public static ArrayList parseAll(String[] numberStrings){
ArrayList myFloats = new ArrayList();
for(int i = 0; i < numberStrings.length; i++){
myFloats.add(new Float(numberStrings[i]));
}
return myFloats;
}
If you want the parseAll() method to return null if there are any errors (like the original example), you'd put the try/catch on the outside like this:
public static ArrayList parseAll1(String[] numberStrings){
ArrayList myFloats = new ArrayList();
try{
for(int i = 0; i < numberStrings.length; i++){
myFloats.add(new Float(numberStrings[i]));
}
} catch (NumberFormatException nfe){
//fail on any error
return null;
}
return myFloats;
}
In reality, you should probably return an error here instead of null, and generally I don't like having multiple returns, but you get the idea.
On the other hand, if you want it to just ignore the problems, and parse whatever Strings it can, you'd put the try/catch on the inside of the loop like this:
public static ArrayList parseAll2(String[] numberStrings){
ArrayList myFloats = new ArrayList();
for(int i = 0; i < numberStrings.length; i++){
try{
myFloats.add(new Float(numberStrings[i]));
} catch (NumberFormatException nfe){
//don't add just this one
}
}
return myFloats;
}
As already mentioned, the performance is the same. However, user experience isn't necessarily identical. In the first case, you'll fail fast (i.e. after the first error), however if you put the try/catch block inside the loop, you can capture all the errors that would be created for a given call to the method. When parsing an array of values from strings where you expect some formatting errors, there are definitely cases where you'd like to be able to present all the errors to the user so that they don't need to try and fix them one by one.
If its an all-or-nothing fail, then the first format makes sense. If you want to be able to process/return all the non-failing elements, you need to use the second form. Those would be my basic criteria for choosing between the methods. Personally, if it is all-or-nothing, I wouldn't use the second form.
As long as you are aware of what you need to accomplish in the loop you could put the try catch outside the loop. But it is important to understand that the loop will then end as soon as the exception occurs and that may not always be what you want. This is actually a very common error in Java based software. People need to process a number of items, such as emptying a queue, and falsely rely on an outer try/catch statement handling all possible exceptions. They could also be handling only a specific exception inside the loop and not expect any other exception to occur. Then if an exception occurs that is not handled inside the loop then the loop will be "preemted", it ends possibly prematurely and the outer catch statement handles the exception.
If the loop had as its role in life to empty a queue then that loop very likely could end before that queue was really emptied. Very common fault.
In your examples there is no functional difference. I find your first example more readable.
You should prefer the outer version over the inner version. This is just a specific version of the rule, move anything outside the loop that you can move outside the loop. Depending on the IL compiler and JIT compiler your two versions may or may not end up with different performance characteristics.
On another note you should probably look at float.TryParse or Convert.ToFloat.
If you put the try/catch inside the loop, you'll keep looping after an exception. If you put it outside the loop you'll stop as soon as an exception is thrown.
My perspective would be try/catch blocks are necessary to insure proper exception handling, but creating such blocks has performance implications. Since, Loops contain intensive repetitive computations, it is not recommended to put try/catch blocks inside loops. Additionally, it seems where this condition occurs, it is often "Exception" or "RuntimeException" which is caught. RuntimeException being caught in code should be avoided. Again, if if you work in a big company it's essential to log that exception properly, or stop runtime exception to happen. Whole point of this description is PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
setting up a special stack frame for the try/catch adds additional overhead, but the JVM may be able to detect the fact that you're returning and optimize this away.
depending on the number of iterations, performance difference will likely be negligible.
However i agree with the others that having it outside the loop make the loop body look cleaner.
If there's a chance that you'll ever want to continue on with the processing rather than exit if there an invalid number, then you would want the code to be inside the loop.
If it's inside, then you'll gain the overhead of the try/catch structure N times, as opposed to just the once on the outside.
Every time a Try/Catch structure is called it adds overhead to the execution of the method. Just the little bit of memory & processor ticks needed to deal with the structure. If you're running a loop 100 times, and for hypothetical sake, let's say the cost is 1 tick per try/catch call, then having the Try/Catch inside the loop costs you 100 ticks, as opposed to only 1 tick if it's outside of the loop.
The whole point of exceptions is to encourage the first style: letting the error handling be consolidated and handled once, not immediately at every possible error site.
put it inside. You can keep processing (if you want) or you can throw a helpful exception that tells the client the value of myString and the index of the array containing the bad value. I think NumberFormatException will already tell you the bad value but the principle is to place all the helpful data in the exceptions that you throw. Think about what would be interesting to you in the debugger at this point in the program.
Consider:
try {
// parse
} catch (NumberFormatException nfe){
throw new RuntimeException("Could not parse as a Float: [" + myString +
"] found at index: " + i, nfe);
}
In the time of need you will really appreciate an exception like this with as much information in it as possible.
I's like to add my own 0.02c
about two competing considerations when looking at the general problem of where to position exception handling:
The "wider" the responsibility of the
try-catch
block (i.e. outside the loop in your case) means that when changing the code at some later point, you may mistakenly add a line which is handled by your existingcatch
block; possibly unintentionally. In your case, this is less likely because you are explicitly catching aNumberFormatException
The "narrower" the responsibility of the
try-catch
block, the more difficult refactoring becomes. Particularly when (as in your case) you are executing a "non-local" instruction from within thecatch
block (thereturn null
statement).
That depends on the failure handling. If you just want to skip the error elements, try inside:
for(int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
} catch (NumberFormatException ex) {
--i;
}
}
In any other case i would prefer the try outside. The code is more readable, it is more clean. Maybe it would be better to throw an IllegalArgumentException in the error case instead if returning null.
I'll put my $0.02 in. Sometimes you wind up needing to add a "finally" later on in your code (because who ever writes their code perfectly the first time?). In those cases, suddenly it makes more sense to have the try/catch outside the loop. For example:
try {
for(int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
dbConnection.update("MY_FLOATS","INDEX",i,"VALUE",myNum);
}
} catch (NumberFormatException ex) {
return null;
} finally {
dbConnection.release(); // Always release DB connection, even if transaction fails.
}
Because if you get an error, or not, you only want to release your database connection (or pick your favorite type of other resource...) once.
Another aspect not mentioned in the above is the fact that every try-catch has some impact on the stack, which can have implications for recursive methods.
If method "outer()" calls method "inner()" (which may call itself recursively), try to locate the try-catch in method "outer()" if possible. A simple "stack crash" example we use in a performance class fails at about 6,400 frames when the try-catch is in the inner method, and at about 11,600 when it is in the outer method.
In the real world, this can be an issue if you're using the Composite pattern and have large, complex nested structures.
If you want to catch Exception for each iteration, or check at what iteration Exception is thrown and catch every Exceptions in an itertaion, place try...catch inside the loop. This will not break the loop if Exception occurs and you can catch every Exception in each iteration throughout the loop.
If you want to break the loop and examine the Exception whenever thrown, use try...catch out of the loop. This will break the loop and execute statements after catch (if any).
It all depends on your need. I prefer using try...catch inside the loop while deploying as, if Exception occurs, the results aren't ambiguous and loop will not break and execute completely.
참고URL : https://stackoverflow.com/questions/141560/should-try-catch-go-inside-or-outside-a-loop
'IT story' 카테고리의 다른 글
파이썬에서 구분 기호로 문자열 나누기 (0) | 2020.05.24 |
---|---|
PHP 7에서 <=> ( '우주선'연산자)는 무엇입니까? (0) | 2020.05.24 |
컴포넌트 외부에서 Vue.js 컴포넌트 메소드 호출 (0) | 2020.05.24 |
ReactJS에서 'onKeyPress'이벤트를 처리하는 방법은 무엇입니까? (0) | 2020.05.24 |
필드 이름 주위에 백틱 사용 (0) | 2020.05.24 |