source

포획은 루프 안쪽으로 할까요, 아니면 바깥쪽으로 할까요?

gigabyte 2022. 7. 23. 13:57
반응형

포획은 루프 안쪽으로 할까요, 아니면 바깥쪽으로 할까요?

다음과 같은 루프가 있습니다.

for (int i = 0; i < max; i++) {
    String myString = ...;
    float myNum = Float.parseFloat(myString);
    myFloats[i] = myNum;
}

이것은 플로트 배열을 반환하는 것이 유일한 목적인 방법의 주요 내용입니다. .null에는 루프를 에, a, a, a, 프, 프 if if a a a a a a a a a a a a a a a a a a a a a a a a.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 내부에 삽입하는 것이 더 깔끔하다는 데 의견이 일치합니다.아마도 자체 메서드에 삽입할 수 있습니다.하지만, 어떤 것이 더 빠른지에 대해서는 여전히 논란이 있다.누가 이걸 테스트하고 통일된 답을 가지고 올 수 있나요?

퍼포먼스:

테스트/캐치 구조가 배치되는 위치에는 성능 차이가 전혀 없습니다.내부적으로는 메서드가 호출될 때 생성되는 구조에서 코드 범위 테이블로 구현됩니다.이 메서드가 실행되는 동안 시도/캐치 구조는 슬로우가 발생하지 않는 한 완전히 그림에서 벗어나며 에러의 위치를 테이블과 비교합니다.

다음은 참고 자료입니다.http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

이 표는 절반 정도 아래쪽에 설명되어 있습니다.

퍼포먼스:Jeffrey가 회답에서 말한 처럼 Java에서는 큰 차이가 없습니다.

일반적으로 코드를 읽기 쉽게 하기 위해 루프가 처리를 계속할지 여부에 따라 예외를 포착할 위치를 선택할 수 있습니다.

이 예에서는 예외를 발견하면 되돌아갑니다.그런 경우라면 시도/캐치를 순환시킬 수 있습니다.단순히 나쁜 값을 잡으려고 하지만 가공을 계속한다면, 안에 넣으세요.

세 번째 방법: 항상 자신의 정적 Parse Float 메서드를 작성하여 루프가 아닌 해당 메서드로 예외 처리를 처리할 수 있습니다.예외 처리를 루프 자체에 격리합니다!

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가 성능 차이가 없다고 말한 후, 저는 가서 테스트했습니다.다음과 같은 작은 벤치마크를 실시했습니다.

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에서는 성능 차이가 전혀 없었습니다(다른 버전에 액세스할 수 없었습니다).전체 테스트에서 총 시간 차이는 약 몇 밀리초입니다.

따라서 가장 깨끗해 보이는 것이 유일한 고려 사항입니다.나는 두 번째 방법이 추하다고 느꼈기 때문에 첫 번째 방법과 레이 헤이스의 방법 중 하나를 고수할 것이다.

퍼포먼스는 동일할 수 있고, 「외관」이 뛰어난 것은 매우 주관적이지만, 기능에는 큰 차이가 있습니다.다음 예를 들어 보겠습니다.

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");
    }

루프가 트라이 캐치 블록 내에 있는 동안 변수 '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 루프 내에 있다는 것입니다.

다음으로 출력(트라이/캐치 중)을 나타냅니다.

4
8
12 
16
in catch block

다른 출력(try/catch in while):

4
8
12
16
in catch block
24
28
32
36
40

여기에는 상당한 차이가 있습니다.

시도/실패 중에 고리를 벗어나다

루프를 활성화 상태로 유지한 상태에서 시도/로그인

퍼포먼스와 가독성 투고에 모두 동의합니다.하지만, 그것이 정말로 중요한 경우들이 있다.다른 사람도 몇 명 언급했지만, 예를 들면 더 쉽게 알 수 있을 것 같습니다.

약간 변경된 예를 다음에 나타냅니다.

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;
}

parseAll() 메서드에서 오류가 발생한 경우(원래 예시와 같이) null을 반환하려면 다음과 같이 try/catch를 외부에 배치합니다.

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;
}

실제로는 여기서 오류를 반환해야 할 것입니다.일반적으로 저는 여러 번 반환하는 것을 좋아하지 않지만 이해하실 수 있습니다.

한편, 문제를 무시하고 가능한 모든 문자열을 해석하려면 다음과 같이 루프 내부에 try/catch를 배치합니다.

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;
}

이미 언급했듯이 성능은 동일합니다.다만, 유저 익스피리언스가 반드시 같은 것은 아닙니다.첫 번째 경우(즉, 첫 번째 오류 이후)는 빠르게 실패하지만, 루프 내에 try/catch 블록을 배치하면 메서드에 대한 특정 호출에 대해 생성되는 모든 오류를 캡처할 수 있습니다.포맷 오류가 발생할 것으로 예상되는 문자열에서 값 배열을 해석할 때 사용자가 일일이 수정하려고 하지 않도록 모든 오류를 사용자에게 제시할 수 있는 경우가 있습니다.

all or nothing 이 실패하면 첫 번째 포맷이 의미가 있습니다.실패하지 않는 모든 요소를 처리하거나 반환하려면 두 번째 양식을 사용해야 합니다.그것이 방법 중 하나를 선택하는 나의 기본 기준이 될 것이다.개인적으로, 만약 그것이 전부 아니면 아무것도 아니라면, 나는 두 번째 양식을 사용하지 않을 것이다.

루프에서 무엇을 달성해야 하는지 알고 있는 한 루프 외부에 try catch를 배치할 수 있습니다.단, 예외 발생 즉시 루프가 종료되고 그것이 항상 필요한 것은 아니라는 것을 이해하는 것이 중요합니다.이는 Java 기반 소프트웨어에서 실제로 매우 일반적인 오류입니다.큐를 비우는 등의 여러 항목을 처리하고 가능한 모든 예외를 처리하는 외부 try/catch 문에 잘못 의존해야 합니다.또한 루프 내에서 특정 예외만 처리하고 다른 예외는 발생하지 않을 수도 있습니다.루프 내부에서 처리되지 않는 예외가 발생하면 루프가 "프리미트"되어 조기에 종료되고 외부 catch 문이 예외를 처리합니다.

큐를 비우는 역할을 루프가 가지고 있다면 큐가 실제로 비기 전에 루프가 종료될 수 있습니다.매우 흔한 결함입니다.

적절한 예외 처리를 위해 try/catch blocks가 필요하지만 이러한 blocks를 작성하면 퍼포먼스에 영향이 있다고 생각합니다.루프에는 대량의 반복 계산이 포함되어 있기 때문에 루프 내부에 try/catch 블록을 삽입하는 것은 권장되지 않습니다.이 곳은 수 .예외"가 검출됩니다.(Runtime) 예외는코드로 합니다.예외는 코드에 걸리지 않도록 해야 합니다.대기업에서 일하는 경우 해당 예외를 적절히 기록하거나 런타임 예외가 발생하지 않도록 중지하는 것이 중요합니다.은 '다'입니다.PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

이 예에서는 기능상의 차이는 없습니다.나는 너의 첫 번째 예가 더 읽기 쉽다고 생각한다.

내부 버전보다 외부 버전을 선호해야 합니다.이것은 규칙의 특정 버전일 뿐입니다.루프 밖으로 이동할 수 있는 것은 루프 밖으로 이동합니다.IL 컴파일러와 JIT 컴파일러에 따라서는 두 버전의 퍼포먼스 특성이 다를 수도 있고 그렇지 않을 수도 있습니다.

다른 점에 대해서는 플로트를 살펴봐야 할 것 같습니다.TryParse 또는 Convert.토플로트

루프 내에 try/catch를 넣으면 예외 후에도 루프가 계속됩니다.루프를 벗어나면 예외가 발생하는 즉시 중지됩니다.

만의 것을 편이에요.0.02c예외 처리를 배치하는 일반적인 문제를 검토할 때 고려해야 할 두 가지 사항에 대해 설명합니다.

  1. '배당'의 책무엇인가?try-catchblock은 나중에 할 때 블록(예: " " " " " )에서할 수 합니다.catch차단, 의도하지 않을 수 있습니다. 경우,을 하고 에 그 .NumberFormatException

  2. 축소'는 '의이다.try-catch이치노 () '를 '로컬' 에서 실행하고 catch)return null이치노

각 반복에 대해 예외를 캐치하거나 어떤 반복에서 예외가 발생하는지 확인하고 반복의 모든 예외를 캐치하려면 루프 내부에 try...catch를 배치합니다.예외 발생 시 루프가 중단되지 않으며 루프 전체에 걸쳐 각 반복의 모든 예외를 포착할 수 있습니다.

루프를 끊고 던질 때마다 예외를 조사하려면 try...catch of the loop을 사용합니다.그러면 루프가 끊어지고 catch 후에 문이 실행됩니다(있는 경우).

모든 것은 당신의 필요에 달려있다.전개 중에 try...catch를 사용하는 것이 좋습니다.예외가 발생했을 경우 결과가 애매하지 않고 루프가 완전히 중단되어 실행되지 않기 때문입니다.

테스트/캐치용으로 특수 스택 프레임을 설정하면 추가 오버헤드가 발생하지만 JVM은 반환 사실을 감지하여 이를 최적화할 수 있습니다.

반복 횟수에 따라 성능 차이는 무시할 수 있습니다.

하지만 루프 외부에 있으면 루프 본체가 더 깨끗해 보인다는 다른 의견에는 동의합니다.

유효하지 않은 번호가 있는 경우 종료하지 않고 처리를 계속할 가능성이 있는 경우 코드를 루프 내에 넣어야 합니다.

내부에 있는 경우, 외부에서 단 한 번이 아닌 N회 시도/캐치 구조의 오버헤드를 얻을 수 있습니다.


Try/Catch 구조가 호출될 때마다 메서드 실행에 오버헤드가 추가됩니다.구조에 대처하려면 메모리와 프로세서의 작은 눈금만 있으면 됩니다.예를 들어 루프를 100회 실행하고 있는 경우, 예를 들어 1회의 시행/캐치 콜당 비용이 1틱이라고 가정해 보겠습니다.루프 내에 Try/Catch를 설치하면 100틱의 비용이 듭니다.루프 외부에 있는 경우는 1틱밖에 들지 않습니다.

예외의 요점은 첫 번째 방식을 장려하는 것입니다.즉, 에러처리를 가능한 모든 에러사이트에서 즉시 실시하는 것이 아니라, 한 번 통합해 처리하는 것입니다.

안에 넣어주세요.처리를 계속할 수도 있고(필요한 경우), 클라이언트에 myString 값 및 잘못된 값을 포함하는 배열 인덱스를 알려주는 유용한 예외를 발생시킬 수도 있습니다.넘버라고 생각합니다.Format Exception은 이미 잘못된 값을 알려 주지만 원칙적으로 유용한 모든 데이터를 사용자가 지정한 예외에 배치하는 것입니다.프로그램의 이 시점에서 디버거에서 어떤 것이 흥미로울지 생각해 보세요.

고려사항:

try {
   // parse
} catch (NumberFormatException nfe){
   throw new RuntimeException("Could not parse as a Float: [" + myString + 
                              "] found at index: " + i, nfe);
} 

필요할 때 가능한 한 많은 정보가 포함된 이러한 예외에 대해 감사할 것입니다.

이는 장애 처리에 따라 달라집니다.에러 요소를 건너뛰는 경우는, 다음의 순서로 시험해 주세요.

for(int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    } catch (NumberFormatException ex) {
        --i;
    }
}

다른 경우라면 밖에서 하는 게 더 좋아요.코드가 더 읽기 쉽고 깨끗합니다.Illigal Argument를 던지는 것이 더 나을 수 있습니다.대신 null을 반환하는 경우 에러 케이스의 예외입니다.

0.02달러를 넣겠습니다.나중에 코드에 "최종"을 추가해야 하는 경우가 있습니다(처음에는 누가 코드를 완벽하게 작성합니까?).이 경우 갑자기 루프 외부에서 try/catch를 실행하는 것이 더 적절합니다.예를 들어 다음과 같습니다.

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.
}

오류가 발생하거나 발생하지 않는 경우 데이터베이스 연결을 한 번만 해제(또는 다른 유형의 리소스를 선택...)해야 하기 때문입니다.

위에서 언급하지 않은 또 다른 측면은 모든 트라이캐치가 스택에 어느 정도 영향을 미쳐 재귀 메서드에 영향을 미칠 수 있다는 점입니다.

메서드 "outer()"가 메서드 "inner()"를 호출하는 경우(재귀적으로 호출할 수 있음), 가능하면 메서드 "outer()"에서 try-catch를 찾습니다.퍼포먼스 클래스에서 사용하는 단순한 "스택 크래시" 예는 트라이캐치가 내부 메서드일 경우 약 6,400프레임, 외부 메서드일 경우 약 11,600프레임에서 실패합니다.

실제로 복합 패턴을 사용하고 크고 복잡한 중첩 구조를 가진 경우 이 문제가 발생할 수 있습니다.

언급URL : https://stackoverflow.com/questions/141560/should-try-catch-go-inside-or-outside-a-loop

반응형