문제

나는 루프는 다음과 같이 나타납니다.

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

어떤 이유가 있습,성능이나 그렇지 않으면,을 선호하니까?


편집: 합의 것 같다는 그는 청소기를 넣어 루프 안에 시도/잡을 가능하게 내부에 자신의 방법입니다.그러나,여전히 논쟁에 더 빠릅니다.할 수 있는 누군가가 이것을 테스트하고 돌아와 통합 대답은?

도움이 되었습니까?

해결책 3

좋아, 후 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 최적화를 가정하면 제프리가 맞습니다; 절대적으로 있습니다 Java 6, Sun Client VM의 성능 차이 없음 (다른 버전에 액세스 할 수 없었습니다). 총 시차는 전체 테스트에서 몇 밀리 초의 순서입니다.

따라서 유일한 고려 사항은 가장 깨끗해 보이는 것입니다. 두 번째 방법은 추악하다는 것을 알았으므로 첫 번째 방법을 고수하거나 레이 헤이즈의 길.

다른 팁

성능:

절대로 성과에 차이가 try/catch 구조에 배치됩니다.내부적으로,그들은 그들로 구현되는 코드 범위의 테이블에서는 구조를 만들 때 방법이라고합니다.는 동안 메소드가 실행,try/catch 구조은 완전히 그림을하지 않으면 던져가 발생한 다음의 위치에 오류가에 대해 비교 테이블.

여기에 참조: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

테이블이 설명된 절반 방법에 대한니다.

성능:로 제프리 에서 말했다 그의 대답에서 Java 지 않게 많은 차이입니다.

일반적으로, 한 코드를 쉽게 읽을 수 있도록,당신의 선택의를 잡을 수있는 예외에 따라 달라집할지 여부를하게 유지하기 위해 루프를 처리하지 않습니다.

에서 예를 들어 당신은 돌에 잡는 예외입니다.이 경우에 넣는 것이 좋 try/을 잡아 루프 주위에.는 경우에 당신은 단순히 잡으려면 나쁜 값만 수행에 처리,안에 넣어.

세 번째 방법:당신은 항상 수 있는 자신의 정서는 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;
    }
}

성능은 동일하고 "외모"가 매우 주관적이지만 기능에는 여전히 큰 차이가 있습니다. 다음 예를 들어보세요 :

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 루프는 시도 캐치 블록 내부에 있고, 변수 '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"); }
    }

위와 동일한 논리는 시도/캐치 블록이 이제 while 루프 내부에 있다는 것입니다.

다음은 출력이 나옵니다 (시도/캐치 중) :

4
8
12 
16
in catch block

그리고 다른 출력 (시도/캐치) :

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을 반환하려면 다음과 같이 시도/캐치를 외부에 올려 놓을 수 있습니다.

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

실제로, 당신은 아마도 null 대신 오류를 반환해야 할 것입니다. 일반적으로 여러 반품을 좋아하지 않지만 아이디어를 얻습니다.

반면에, 문제를 무시하고 가능한 문자열을 구문 분석하기를 원한다면 시도/캐치를 루프 내부에 넣을 것입니다.

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 외부합니다.하지만 그것을 이해하는 것이 중요하다는 반복음 끝으로 예외가 발생하고는 항상하지 않을 수 있습니다.이것은 실제로 매우 일반적인 오류가에서는 Java 기반의 소프트웨어입니다.사람들이 필요로하는 프로세스는 항목 수와 같은 비우는 큐,그리고 거짓으로에 의존하고 있는 외부 시도/catch 문 처리 가능한 모든 예외가 있습니다.그들은 또한 취급을 특정 예외 루프 안에서 그 기대하지 않는 다른 예외를 발생합니다.그는 예외가 발생하면 처리하지 않은 루프 안에서 다 루프 될 것이다"preemted",그것은 끝이 가능한 조기에 외부 catch 문 손잡이는 예외입니다.

는 경우 루프로 역할을에서 생활하는 빈 큐 그는 반복 가능성이 매우 높을 끝낼 수 있었기 전에는 큐었습니다.매우 일반적인 잘못입니다.

예에서는 기능적 차이가 없습니다. 나는 당신의 첫 번째 예가 더 읽기 쉬운 것을 발견했습니다.

내부 버전보다 외부 버전을 선호해야합니다. 이것은 규칙의 특정 버전 일뿐입니다. 루프 외부로 이동할 수있는 루프 외부를 이동하십시오. IL 컴파일러 및 JIT 컴파일러에 따라 두 버전은 다른 성능 특성으로 끝나지 않을 수 있습니다.

다른 메모에서는 아마도 float.tryparse 또는 convert.tofloat를보아야 할 것입니다.

시도/캐치를 루프 내부에 넣으면 예외 후에 루핑이 계속됩니다. 루프 밖에 놓으면 예외가 발생하자마자 멈출 수 있습니다.

내 관점은 적절한 예외 처리를 보장하기 위해 시도/캐치 블록이 필요하지만 이러한 블록을 만드는 것은 성능에 영향을 미칩니다. 루프에는 집중적 인 반복 계산이 포함되어 있기 때문에 시도/캐치 블록을 루프 내부에 넣는 것이 좋습니다. 또한,이 조건이 발생하는 곳이있는 것처럼 보이며, 종종 "예외"또는 "runtimeexception"이 잡히는 것처럼 보입니다. 코드에 잡힌 runtimeexception은 피해야합니다. 다시 한 번 대기업에서 일하는 경우 해당 예외를 올바르게 기록하거나 런타임 예외를 중지해야합니다. 이 설명의 요점은 다음과 같습니다 PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

시도/캐치를위한 특수 스택 프레임을 설정하면 추가 오버 헤드가 추가되지만 JVM은 반환한다는 사실을 감지하고이를 최적화 할 수 있습니다.

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

그러나 나는 루프 외부에 그것을 갖는 것이 루프 본체를 깨끗하게 보이게한다는 것에 동의합니다.

유효하지 않은 숫자가 있으면 종료하기보다는 처리를 계속하고 싶을 가능성이 있다면 코드가 루프 내부에 있기를 원할 것입니다.

내부에 있다면, 외부의 한 번과 달리 시도/캐치 구조 n 번의 오버 헤드를 얻을 수 있습니다.


시도/캐치 구조를 호출 할 때마다 메소드 실행에 오버 헤드가 추가됩니다. 구조를 다루는 데 필요한 약간의 메모리 및 프로세서 진드기 만 있습니다. 루프를 100 회 실행하고 가상을 위해 비용이 1 개의 시도/캐치 콜 당 1 진드기라고 가정 해 보면 루프 내부에 시도/캐치가 100 번의 틱이 필요하다고 가정 해 봅시다. 루프 외부.

예외의 요점은 첫 번째 스타일을 장려하는 것입니다. 모든 가능한 오류 사이트에서 즉시가 아니라 오류 처리를 한 번 통합하고 처리하도록합니다.

안에 넣어.유지할 수 있습니다 처리(하려는 경우)또는할 수 있습니다 도움이 되는 예외 클라이언트의 값을 리턴과 배열의 인덱스를 포함하는 나쁜 값입니다.나는 생각 NumberFormatException 이미 당신에게 나쁘지만 원칙은 배치의 모든 도움이 되는 데이터에서는 예외를 던져.될 것 이라고 생각해 당신에게 흥미로운 디버거에서 이 시점에서 프로그램입니다.

생각해 보십시오:

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

에서의 시간이 필요 당신이 정말로 감사하는 예외적으로 많은 정보에서 그것으로 가능합니다.

나는 내 자신을 추가하고 싶다 0.02c 예외 처리 위치의 일반적인 문제를 살펴볼 때 두 가지 경쟁 고려 사항에 대해서는 다음과 같습니다.

  1. "더 넓은"책임 try-catch 블록 (즉, 케이스의 루프 외부)은 나중에 코드를 변경할 때 실수로 기존에서 처리하는 줄을 추가 할 수 있음을 의미합니다. catch 차단하다; 아마도 의도하지 않을 것입니다. 귀하의 경우, 이것은 당신이 명시 적으로 잡고 있기 때문에 가능성이 적습니다. NumberFormatException

  2. "더 좁은"책임 try-catch 블록, 더 어려운 리팩토링이됩니다. 특히 (귀하의 경우와 같이) 내에서 "비 국한적"명령을 실행하는 경우 catch 블록 ( return null 성명).

이는 실패 처리에 따라 다릅니다. 오류 요소를 건너 뛰려면 내부를 시도하십시오.

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

다른 경우에는 외부의 시도를 선호합니다. 코드는 더 읽기 쉽고 더 깨끗합니다. 아마도 null을 반환하는 경우 오류 케이스에서 불법 행위를 던지는 것이 더 낫습니다.

나는 내 $ 0.02를 넣을 것이다. 때로는 당신은 나중에 코드에 "마지막으로 코드를 완벽하게 작성하기 때문에"나중에 "마지막으로"를 추가해야합니까? 그러한 경우 갑자기 루프 밖에서 시도/캐치를 갖는 것이 더 합리적입니다. 예를 들어:

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

오류가 발생하거나 그렇지 않으면 데이터베이스 연결을 해제하거나 좋아하는 유형의 다른 리소스를 한 번만 선택하십시오 ...).

위에서 언급하지 않은 또 다른 측면은 모든 Try-Catch가 약간 재귀 적 방법에 영향을 줄 수있는 스택에 미치는 영향.

메소드 "outer ()"콜 메소드 "inner ()"(재귀 적으로 호출 될 수 있음) 인 경우 가능한 경우 메소드 "OUTER ()"에서 Try-Catch를 찾으십시오. 성능 클래스에서 사용하는 간단한 "스택 충돌"예제는 Try-Catch가 내부 방법에있을 때 약 6,400 프레임에서, 그리고 외부 방법에있을 때 약 11,600에서 실패합니다.

실제 세계에서는 복합 패턴을 사용하고 크고 복잡한 중첩 구조를 사용하는 경우 문제가 될 수 있습니다.

각 반복에 대한 예외를 포착하거나 반복 예외를 확인하고 Itertaion의 모든 예외를 잡으려면 ... 루프 내부를 잡아보십시오. 예외가 발생하면 루프가 나지 않으며 루프 전체의 각 반복에서 모든 예외를 포착 할 수 있습니다.

루프를 끊고 던질 때마다 예외를 검사하려면 시도해보십시오. 이렇게하면 루프가 깨지고 캐치 후 명령문을 실행합니다 (있는 경우).

그것은 모두 당신의 필요에 달려 있습니다. 나는 시도를 선호합니다 ... 예외가 발생하면 결과가 모호하지 않으며 루프가 완전히 깨지고 실행되지 않으므로 루프 내부를 잡아 당깁니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top