다음과 같은 루프가 있습니다.
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;
}
오류가있는 경우 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;
}
실제로는 null 대신 오류를 반환해야하며 일반적으로 여러 번 반환하는 것을 좋아하지 않지만 아이디어를 얻습니다.
반면에 문제를 무시하고 문자열을 구문 분석하려면 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 블록을 루프 안에 넣으면 주어진 메소드 호출에 대해 생성 된 모든 오류를 캡처 할 수 있습니다. 일부 형식화 오류가 예상되는 문자열 값 배열을 구문 분석 할 때 모든 오류를 사용자에게 제시하여 하나씩 수정하려고 할 필요가없는 경우가 있습니다. .
답변
그것의 전부 또는 아무것도 실패하면, 첫 번째 형식이 의미가 있습니다. 고장이 아닌 모든 요소를 처리 / 반품하려면 두 번째 양식을 사용해야합니다. 그것들은 방법 중에서 선택하기위한 나의 기본 기준이 될 것입니다. 개인적으로, 아니면 아무것도 아니면, 나는 두 번째 양식을 사용하지 않을 것입니다.